高级实训
微服务简介
分布式简介
springboot使用
springboot是一个框架大集合,包含我们常见的大部分框架,spring官方已经替我们集成好了,我们不需要写任何的配置文件,直接dependency依赖即可。spring官方提供了一套快速建立应用的机制就是springboot
创建/运行/打包
maven辅助工具:创建工程,配置工程(jar包,tomcat),运行工程,打包工程
maven中国镜像:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
maven中央仓库:
maven tomcat插件:
<!--插件 tomcat-->
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path> <!-- 项目访问路径 本例:localhost:9090, 如果配置的aa,则访问路径为localhost:9090/aa -->
<port>8086</port>
<uriEncoding>UTF-8</uriEncoding><!-- 非必需项 -->
</configuration>
</plugin>
</plugins>
打包:
jar/war
jar:第三方的人写好的工程,编译成 jar包,我们得到jar包,可以在工程中直接调用其中的类库
war:web包,可以放到tomcat的webapps文件夹中,直接运行。xxxxxx.war
springboot官方推荐打成jar包方式,当然也支持war包。
springboot启动jar的方式为:
快捷提示的tab键
java -jar demo5-0.0.1-SNAPSHOT.jar
springboot配置文件
配置文件中可以 配置springboot的各个框架的配置信息
例如:tomcat端口,访问路径,数据库用户名/密码,等等
idea有自动提示,不要记忆
详细的配置请见官方文档:
附录A
YML语法
springboot的配置文件支持两种格式;
一种是:xxx.properties
另一种是:xxx.yml
server:
port: 8087
servlet:
context-path: /hhh
注意:
yml的语法是k: v v前面必须有一个空格,否则无效。
配置文件注入
使用@ConfigurationProperties(prefix = "person")从我们的配置文件中读取用户配置数据,该注解支持yml和properties
@Component注解和导入配置文件无关,作用是把我们的person类放入到spring的容器 中(注册成bean)
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private int age;
private String name;
private boolean sex;
List<String > list;
Map<String,Object> map;
Cat cat;
//.......getter setter tostring
}
public class Cat {
private int age;
private String name;
//......
}
@Controller
public class HelloController {
//从容器中取出person的值
@Autowired
Person person;
// springboot的 方法比直接maven工程更加快速
@ResponseBody
@RequestMapping("hello")
//hello和/hello效果一样
public String hello(){
System.out.println(person);
return "hello everyone";
}
}
server:
port: 8087
servlet:
context-path: /hhh
#作用;给用户留出配置的接口,用户配置成什么,那么我们的程序运行的时候就是什么值
person:
age: 30
name: zhangsan
sex: true
list:
- aaaa
- bbbb
- cccc
map:
f: fffff
g: ggggg
cat:
age: 2
name: ahuang
person.age=40
person.name=lisi
person.sex=true
person.list=qqq,www,eee
person.map.k1=kkkkk
person.map.k2=ttttt
person.cat.age=1
person.cat.name=ahei
第二种注入值的方式:
@Value
@Value("${mysql.mypwd}")
String pwd;
@ConfigurationProperties | @Value |
---|---|
支持多数据导入 | 单一数据导入 |
支持参数校验 | 不支持参数校验 |
参数校验
springmvc参数校验
@ConfigurationProperties(prefix = "person")支持参数校验
参数校验:验证参数的类型是否合规。
注解 | 说明 | 备注 |
---|---|---|
AssertTrue | 标注元素必须为true | boolean,Boolean,Null |
AssertFalse | 标注元素必须为false | boolean,Boolean,Null |
DecimalMax(value,isclusive) | 标注元素必须小于等于指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
DecimalMin(value,isclusive) | 标注元素必须大于等于指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
Digits(integer,fraction) | 标注元素必须位于指定位数之内 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
Email(regexp,flags) | 标注元素必须为格式正确的邮件地址 | CharSequence |
Future | 标注元素必须为将来的日期 | Date,Calendar,Instant, LocalDate,LocalDateTime, LocalTime,MonthDay, OffsetDateTime,OffsetTime, Year,YearMonth, ZonedDateTime,HijrahDate, JapaneseDate,MinguoDate, ThaiBuddhistDate |
FutureOrPresent | 标注元素必须为现在或将来的日期 | 同Future |
Max(value) | 标注元素必须小于等于指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
Min(value) | 标注元素必须大于等于指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
Negative | 标注元素必须为严格负值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
NegativeOrZero | 标注元素必须为严格的负值或者0值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
NotBlank | 标注元素必须不为null,且必须包含至少一个非空字符 | CharSequence |
NotEmpty | 标注元素必须不为null,且必须包含至少一个子元素 | CharSequence,Collection,Map,Array |
NotNull | 标注元素必须不为null | all |
Null | 标注元素必须为null | all |
Past | 标注元素必须为过去的日期 | 同Future |
PastOrPresent | 标注元素必须为过去的或者现在的日期 | 同Future |
Pattern(regexp,flags) | 标注元素必须匹配给定的正则表达式 | CharSequence,Null |
Positive | 标注元素必须为严格的正值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
PositiveOrZero | 标注元素必须为严格的正值或者0值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long,Null |
Size(min,max) | 标注元素必须在指定范围之内 | CharSequence,Collection,Map,Array |
上面的罗列的注解均可作用于方法、字段、构造器、参数,还有注解类型之上,其中作用为注解类型目的就是为了组合多个校验,从而自定义一个组合校验注解。
Hibernate Validation
Hibernate Validation承载自JSR 303的Bean Validation,拥有其所有功能,并对其进行了扩展,它自定义了以下校验注解:
注解 | 说明 | 备注 |
---|---|---|
Length(min,max) | 标注元素的长度必须在指定范围之内,包含最大值 | 字符串 |
Range(min,max) | 标注元素值必须在指定范围之内 | 数字值,或者其字符串形式 |
URL(regexp,flags) | 标注元素必须为格式正确的URL | 字符串 |
URL(protocol,host,port) | 标注元素必须满足给定的协议主机和端口号 | 字符串 |
课下自己可以把这些校验尝试一下。
参数校验实现:
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
@Size(min = 5,max = 20)
private int age;
@Email
private String name;
@NotNull
private boolean sex;
List<String > list;
Map<String,Object> map;
Cat cat;
//..........
}
@Validated注解是说,该类需要参数校验。
具体的校验规则见上文。
第二种情况:我们在springmvc中也经常用到校验
如下:
@RestController
public class VaildateController {
// @ResponseBody
@RequestMapping("test")
public String test(@Validated Cat cat, BindingResult result){
String s = null;
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError e : allErrors) {
System.out.println(e.getDefaultMessage());
s=e.getDefaultMessage();
}
return s;
}
}
public class Cat {
@Min(1)
@Max(3)
private int age;
@Email
private String name;
//.......
}
加载指定配置文件
@PropertySource该注解可以用来加载用户指定的配置文件,但是注意,该注解不支持yml文件的自定义加载。所以,如果我们的工程中需要有一些自己的配置文件,那么后缀请使用properties。
@PropertySource(value = {"classpath:myconfig.properties"})
springboot配置文件的推荐写法
我们在以前的学习过程中,如果使用一个框架,例如mybatis /shiro,我们会讲该框架以xml的方式配置进来。
现在springboot官方推荐使用java类+注解的方式来实现各个框架的配置。
情况一:很多框架springboot官方是默认集成的,详情看spring-boot-starter-xxxxx,那么直接导入denpedency即可,不需要配置。直接用即可。
情况二:有一些框架springboot官方未做集成,例如shiro,我们需要自己写配置。使用@Configration+@Bean来完成。详情见shiro。
配置文件多文档块
在yml文件中:
server:
port: 8087
servlet:
context-path: /hhh
spring:
profiles:
active: dev
# 我们在开发时经常使用这种方式配置多环境
---
server:
port: 8088
spring:
profiles: test
---
server:
port: 8089
spring:
profiles: dev
---
server:
port: 8099
spring:
profiles: deploy
在properties文件中使用如下方式做多文档块。
server.port=8090
spring.profiles.active=test
application.properties;
server.port=9092
application-test.properties;
server.port=9095
application-dev.properties;
随机数
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
作为了解。
配置文件加载位置
SpringBoot从以下位置的 application.properties 文件加载属性,并将它们添加到Spring Environment :
\1. 当前目录
\2. 当前目录的 /config 子目录
\3. 类路径根
\4. 类路径根 /config 包
优先级依次由低到高
springboot日志集成
原理剖析
springboot官方使用的是slf4j来实现日志功能。
springboot是如何统一各个框架的日志的:
首先它会排除其他框架中 自带的日志框架(开发时需要我们注意,我们需要手动去删除其他的日志包,否则会冲突报错)
例如:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
然后将自己的替换包导入(就在logging中)为了防止其他框架因为第一步删除日志包而报错
最后统一使用slf4j做日志输出管理
springboot log日志输出优先级:
error
warn
info
debug
trace
#配置日志输出级别,默认级别是info
logging.level.com.example.baidu=trace
如何配置日志的位置和日志名称
#path:日志的路径
logging.file.path=d://log/xxx/aaa
#name:日志问价名称
#logging.file.name=mylog
注意:path和name两个配置,不可以同时使用,如果同时出现,那么path不生效
控制台日志格式:
logging.pattern.console=%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n
logging.pattern.file=%d{yyyy/MM/dd-HH:mm} [%thread] %-5level %logger- %msg%n
解释
%d{HH:mm:ss.SSS}——日志输出时间
%thread——输出日志的进程名字,这在Web应用以及异步任务处理中很有用
%-5level——日志级别,并且使用5个字符靠左对齐
%logger- ——日志输出者的名字
%msg——日志消息
%n——平台的换行符
日志实现
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
Logger logger = LoggerFactory.getLogger(getClass());
logger.error("error");
logger.info("info");
logger.warn("warn");
logger.debug("debug");
logger.trace("trace");
springboot web集成
json
// springboot中默认使用jackson做json
// @ResponseBody 将结果以字符串的形式输出,但是如果结果是其他数据类型,该注解会自动将其转换成json字符串
// @RequestBody Person p 该注解会将前端传递过来的json字符串,自动解析成对象
// 我们开发时经常搞前后端分离,数据交互一率统一使用json,包括android/ios
静态资源
springboot官方指定的静态资源路径:
/static
/public
/resources
/META-INF/resources
我们可以将css/js/image/video/mp3等资源放入到该文件夹下,系统会自动读取
另一种静态资源导入方式是:webjars
它是以maven的方式导入静态资源
默认路径是/META-INF/resources这个静态资源文件夹
模板引擎thymeleaf
如果要访问template模板中html网页,那么必须引入thymeleaf模板,否则404
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
注意:template模板中的html不可以通过浏览器直接访问。只能通过springboot(springmvc)转发(return)
欢迎页:
index.html页面是系统默认的欢迎页 ,可以放到static静态资源下,也可以放到template下。、
网站图标:
将图片放到static静态文件夹下即可,名称为favicon.ico。
需要注意的是:如果你的工程添加了server.servlet.context-path,那么可能会找不到图片,因为路径不对。
错误页面:
在template文件夹下新建error路径,在其中写入4xx.html和5xx.html网页,当发生404 503等错误的时候,会自动返回该页面。
restful风格
restfull是一种风格
支持put改 delete删 get查 post增
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<script src="webjars/jquery/3.5.1/jquery.js"></script>
</head>
<body>
<button id="delete">删除文章</button>
<button id="add">添加文章</button>
<button id="update">修改文章</button>
<button id="query">查询文章</button>
<script>
$("#delete").click(function () {
$.ajax({
url:"456/artical/76598",
type: "delete",
dataType:"json",
success:function (data) {
console.log(data)
}
});
});
$("#update").click(function () {
$.ajax({
url:"123/artical/76598000",
type: "put",
dataType:"json",
success:function (data) {
console.log(data)
}
});
});
</script>
</body>
</html>
@RestController
public class RestfulController {
@DeleteMapping("/{uid}/artical/{aid}")
public String deleteArtical(@PathVariable String aid,@PathVariable String uid){
System.out.println(aid);
System.out.println(uid);
return "删除成功";
}
@PutMapping("/{uid}/artical/{aid}")
public String putArtical(@PathVariable String aid,@PathVariable String uid){
System.out.println(aid);
System.out.println(uid);
return "删除成功";
}
// @GetMapping
// @PostMapping
}
数据库
jooq
遗留
逆向生成
生成过程:略
使用:
@Autowired
CityMapper cityMapper;
@GetMapping("/allcity")
public List getAllCity(){
CityExample example = new CityExample();
example.createCriteria().andPopulationBetween(10000,100000);
List<City> cities = cityMapper.selectByExample(example);
return cities;
}
错误修改:
1/ 数据库 配置
spring.datasource.url=jdbc:mysql://localhost:3306/world?serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
2/ IDEA-解决: org.apache.ibatis.binding.BindingException:Invalid bound statement (not found)
无法找到mapper.xml文件
因为我们把mapper.xml文件放入到java文件下,默认springboot是不扫描java文件夹下 的xml文件的。
但是我们要让它扫描:
在pom.xml文件中加入:
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
3/在启动类上,加入mybatis接口的扫描注解,用来扫描mybatis的接口,放入到spring的容器中。
@MapperScan("com.example.tedu.dao")
@MapperScan("com.example.tedu.dao")
@SpringBootApplication
public class Demo6Application {
public static void main(String[] args) {
SpringApplication.run(Demo6Application.class, args);
}
}
分页插件
1/导包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>LATEST</version>
</dependency>
2/java
PageHelper.startPage(3,20);
@GetMapping("/allcity")
public List getAllCity(){
CityExample example = new CityExample();
example.createCriteria().andPopulationBetween(10000,100000);
PageHelper.startPage(3,20);
List<City> cities = cityMapper.selectByExample(example);
return cities;
}
shiro使用
https://www.cnblogs.com/feiyu127/p/7700090.html
概念:
sbuject:当前用户
SecurityManager:安全管理者,用户想访问 我们的网站,必须经过它。判断你请求的资源是什么限制类型(anon,authc等),如果是authc认证,那么它会调用realm,去做认证。
realm:shiro用它连接数据源(配置文件,文件,数据库等),我们经常自定义 realm用它连接数据库。
Authentication:认证,加密,加盐,迭代等
Authorization:授权,角色rose,权限permission
在这里插入图片描述springboot集成shiro
1/导包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>LATEST</version>
</dependency>
2/配置shiro,编写 配置文件(java)
package com.example.tedu.shiro;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* Created by colors on 2020/7/15.
* xml配置文件方式在5年前就被抛弃了
*/
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
Map<String,String> map = new HashMap<>();
map.put("/css/**","anon");
map.put("/js/**","anon");
map.put("/images/**","anon");
map.put("/webjars/**","anon");
map.put("/login","anon");
map.put("/loginPage","anon");
map.put("/register","anon");
map.put("/registerPage","anon");
map.put("/page1","anon");
map.put("/page2","anon");
map.put("/logout","logout");
map.put("/**","authc");
// 过滤链
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 认证页面
shiroFilterFactoryBean.setLoginUrl("/loginPage");
// ref
shiroFilterFactoryBean.setSecurityManager(securityManager());
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(customRealm());
return securityManager;
}
// 自定义realm
@Bean
public CustomRealm customRealm(){
CustomRealm customRealm = new CustomRealm();
customRealm.setCredentialsMatcher(credentialsMatcher());
return customRealm;
}
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(10000);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
//加入注解的使用,不加入这个注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
// 启动shiro
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
// //shiro网页标签
// @Bean
// public ShiroDialect shiroDialect() {
// return new ShiroDialect();
// }
}
3/自定义realm
package com.example.tedu.shiro;
import com.example.tedu.dao.UserMapper;
import com.example.tedu.pojo.Permission;
import com.example.tedu.pojo.Role;
import com.example.tedu.pojo.User;
import com.example.tedu.pojo.UserExample;
import com.example.tedu.serivce.LoginService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* Created by colors on 2020/7/15.
* 操作数据库
*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
LoginService loginService;
@Autowired
UserMapper userMapper;
// 认证 获取用户信息 还没有做认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String name = authenticationToken.getPrincipal().toString();
if (name==null){
return null;
}else {
//拿着用户名查询数据库,得到用户信息
User user = loginService.login(name);
if (user==null){
return null;
}else {
// hashedCredentials密码
// 用数据库的数据,做一个认证信息,交给shiro
SimpleAuthenticationInfo authenticationInfo =
new SimpleAuthenticationInfo(name,user.getPassword().toString(), ByteSource.Util.bytes(user.getSalt()),getName());
return authenticationInfo;
}
}
}
// 授权
// shiro中的权限分为role和 permission两种
// 做项目时,数据库设计最重要,底层基础决定上层高度
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
String name = (String) principalCollection.getPrimaryPrincipal();
UserExample example = new UserExample();
example.createCriteria().andUsernameEqualTo(name);
List<User> users = userMapper.selectByExample(example);
if (users.size()!=0){
User user = users.get(0);
Role role = userMapper.queryRole(user.getId());
// 将用户角色信息放入到shiro中
authorizationInfo.addRole(role.getRole());
}
List<Permission> permissions = userMapper.queryPermission(name);
if (permissions.size()!=0){
for (Permission p : permissions) {
authorizationInfo.addStringPermission(p.getPermission());
}
}
return authorizationInfo;
// org.apache.shiro.authz.AuthorizationException
}
// public static void main(String[] args) {
// SimpleHash simpleHash =
// new SimpleHash("md5","123456",
// "v9o]zPD22,IX[IjZAet4cr+\\k]IChe6;fE 'y6qj +9.bGPD^C",100);
//
// System.out.println(simpleHash.toString());
// }
}
4/登录
@Controller
public class LoginController {
@RequestMapping("index")
public String index(){
return "index";
}
@RequestMapping("page1")
public String page1(){
return "page1";
}
@RequestMapping("page2")
public String page2(){
return "page2";
}
@RequiresRoles("sadmin")
@RequestMapping("page3")
public String page3(){
return "page3";
}
@RequestMapping("loginPage")
public String loginPage(){
return "login";
}
@RequestMapping("login")
public String login(User user, Model model, HttpSession session){
// subject存放着你的信息(数据库中的)
Subject subject = SecurityUtils.getSubject();
//做一个令牌token,存放用户的网页用户名和密码
UsernamePasswordToken token =
new UsernamePasswordToken(user.getUsername(),user.getPassword());
// login是shiro替我们做匹配认证
try {
subject.login(token);
}catch (UnknownAccountException e){
model.addAttribute("msg","用户不存在");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
session.setAttribute("name",user.getUsername());
// org.apache.shiro.authc.UnknownAccountException用户不存在
// org.apache.shiro.authc.IncorrectCredentialsException密码错误
return "redirect:index";
}
}
浅尝docker
虚拟机 vm vbox
docker 性能远高于虚拟机,对于磁盘的占用极小,对于cup内存的占用也很小。
docker采用沙箱机制。docker各个沙箱中的软件独立运行,互不干涉。每个沙箱都有自己的ip和端口
docker镜像 我们可以从它的官方仓库下载各种镜像
docker容器 安装的软件叫容器
linux基本操作
首先配置centos网络
可以和主机互通 ping
docker基本命令
可以修改yum源为阿里云
略
yum search docker
yum install docker
启动docker
systemctl start docker
查看以有的镜像
docker images
查看已有的容器
docker ps -a
删除镜像
docker rmi 镜像名
删除容器,如果容器运行中
docker stop 容器id
docker rm 容器id
安装tomcat
可以修改docker镜像为中国
vi /etc/docker/daemon.json
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
systemctl restart docker
从官方仓库下载镜像
docker pull tomcat:版本号(版本号可以省略,默认最新版)
https://hub.docker.com/_/mysql?tab=tags
启动tomcat
docker run -d -p 8081:8080 tomcat
-d后台运行
-p 端口映射 宿主机端口:容器端口
tomcat 镜像名字
查看容器的ip
docker inspect 容器id
进入容器内部
docker exec -it 容器id bash
删除webapps
rm -rf webapps
将webapps.dist改名为webapps
mv webapps.dist webapps
退出容器
exit
重启容器
docker restart 容器id
我们启动两个tomcat
然后修改 其中一个tomcat的默认页面,使我们能够区分俩个tomcat即可
但是,docker容器内部没有vi/vim命令
在容器内部执行
apt-get update
apt-get install vim
如果你的网速更新比较慢,我们可以采用复制的方式
退出docker
将我们需要的文件复制到宿主主机上
docker cp f0c40adac880:/usr/local/tomcat/webapps/ROOT/index.jsp /home
在宿主主机上修改,完成后再复制回去即可
docker cp /home/index.jsp f0c40adac880:/usr/local/tomcat/webapps/ROOT/
重启容器
docker restart f0c40adac880
也可以实现文件修改。
在你运行run容器的时候,加一个命令-v
-v 宿主机的一个配置文件路径/home/xxx.conf 容器的配置文件 /etc/xxx.conf
应该的对docker版本有要求
安装nginx
nginx两个作用:
1/静态资源服务器
开发时,我们一般动静分离部署
一般使用nginx做静态资源服务器,为网站提供静态资源服务
一般使用tomcat做动态资源服务器,提供具体业务服务
docker pull nginx
docker run -d -p 9090:80 nginx
docker exec -it 容器id bash
cd /etc/nginx
该文件夹下的nginx.conf就是nginx的配置文件。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
include /etc/nginx/conf.d/*.conf;
但是该配置文件中没有配置 nginx的信息,最后一行引入了另一个配置文件,在此文件中有nginx的具体配置
将给conf文件复制出来
docker cp c3a47fbcdebf:/etc/nginx/conf.d/default.conf /home
vi /home/deafault.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
2/反向代理服务器
配置nginx做反向代理服务器
include /etc/nginx/conf.d/*.conf;
nginx的真正配置文件就是上述路径中的default.conf
修改 该文件
upstream local_tomcat{
server 172.17.0.3:8080;
server 172.17.0.4:8080;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://local_tomcat;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
将default.conf复制回容器
docker cp /home/default.conf c3a47fbcdebf:/etc/nginx/conf.d/
重启nginx容器
docker restart 容器id
nginx其他配置方式请见博客:
https://www.cnblogs.com/kinwing/p/11130281.html
问题遗留:
如何实现nginx集群化
地区来访问不同的域名 bj.fang.com/sz.fang.com
游戏,分区
微服务:
300模块
聚划算ju.taobao.com 20ip 50 ju.com cart.ju.com order.ju.com 抢购 服装 电器城。。。。
安装mysql
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mariadb
-d 后台
-p端口映射
-e 设置初始密码(不设置,不能启动)
安装redis
docker pull redis
docker run -d -p 7006:6379 redis
进入docker
docker exec -it 24592a4df6d6 bash
进入redis
redis-cli
接下来就是各种命令操作redis了。
安装redis集群
docker中的redis默认是没有配置文件的(redis.conf)
redis默认不开启 集群模式,需要我们在redis.conf中配置。
所以我们在启动集群的时候必须挂载宿主机中的redis.conf(提前配置好,开启集群模式)到redis中
挂载命令-v 宿主机文件:容器中的位置
我的docker中执行-v命令时总是报错,容器可以create,但是无法up;
如果想执行-v而不报错,需要我们关闭系统的一个SELinux。
vim /etc/sysconfig/selinux
SELINUX=enforcing 改为 SELINUX=disabled
安全增强型 Linux(Security-Enhanced Linux)简称 SELinux,它是一个 Linux 内核模块,也是 Linux 的一个安全子系统。
SELinux 主要由美国国家安全局开发。2.6 及以上版本的 Linux 内核都已经集成了 SELinux 模块。
SELinux 的结构及配置非常复杂,而且有大量概念性的东西,要学精难度较大。很多 Linux 系统管理员嫌麻烦都把 SELinux 关闭了。
然后重启centos系统。就ok了。
下一步我们需要得到redis.conf文件
wget http://download.redis.io/releases/redis-6.0.5.tar.gz
tar -zxvf redis-6.0.5.tar.gz
cd redis-6.0.5
就可以得到redis.conf文件
下一步,配置redis.conf文件
bind 127.0.0.1 可以 将其注释掉或者改成bind 0.0.0.0
#启用集群模式 默认注释掉了 重点
cluster-enabled yes
#超时时间appendonly yes
daemonize yes
protected-mode no
下一步运行docker redis容器,启动redis服务器,6台(3主3从)。下面 的命令只是启动了6个服务器,没有配主从等。
docker run -p 7000:6379 -v /myredis/conf/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
docker run -p 7001:6379 -v /myredis/conf/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
docker run -p 7002:6379 -v /myredis/conf/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
docker run -p 7003:6379 -v /myredis/conf/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
docker run -p 7004:6379 -v /myredis/conf/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
docker run -p 7005:6379 -v /myredis/conf/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
下一步配置集群和主从
首先查看6台服务器的ip地址,后面要用(inspect查看容器ip,grep过滤作用)
docker inspect c69f55bda18b 3e0230701a05 af2c1524e494 d294113b8944 6ec9c7ff9118 a81b155cb051 |grep IPAddress
-----------------------------------------------
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.7",
"IPAddress": "172.17.0.7",
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.6",
"IPAddress": "172.17.0.6",
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.5",
"IPAddress": "172.17.0.5",
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.4",
"IPAddress": "172.17.0.4",
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.3",
"IPAddress": "172.17.0.3",
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
-----------------------------------------------
然后启动集群,任意进入一台redis容器
docker exec -it c69f55bda18b bash
执行如下命令创建集群
redis-cli --cluster create 172.17.0.2:6379 172.17.0.3:6379 172.17.0.4:6379 172.17.0.5:6379 172.17.0.6:6379 172.17.0.7:6379 --cluster-replicas 1
#--cluster-replicas 1是配置一主一从,如果是2就是一主二从
结果示意:有个yes需要输入一下
---------------------------------------------------
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.17.0.6:6379 to 172.17.0.2:6379
Adding replica 172.17.0.7:6379 to 172.17.0.3:6379
Adding replica 172.17.0.5:6379 to 172.17.0.4:6379
M: 209df49459b4381369a6df9a1ec15cbad720569c 172.17.0.2:6379
slots:[0-5460] (5461 slots) master
M: 8e4e787885fe0af1419b7c2ef9dadb83b76d579e 172.17.0.3:6379
slots:[5461-10922] (5462 slots) master
M: b19e42dbf88969785e40784638b6386428c0d2b1 172.17.0.4:6379
slots:[10923-16383] (5461 slots) master
S: 8982c04e80b04a312a241f9ae68539160dc90e61 172.17.0.5:6379
replicates b19e42dbf88969785e40784638b6386428c0d2b1
S: 8141634b2b7eddc49494d9a6db1a54f355a2931a 172.17.0.6:6379
replicates 209df49459b4381369a6df9a1ec15cbad720569c
S: b4ecb477f53c59a3448286051778106e261ee3cf 172.17.0.7:6379
replicates 8e4e787885fe0af1419b7c2ef9dadb83b76d579e
Can I set the above configuration? (type 'yes' to accept): yes
---------------------------------------------------
如何查看集群信息
首先进入redis命令行内部
执行:redis-cli
进入后再执行 cluster info可以看到集群信息
-----------------------------------------
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:104
cluster_stats_messages_pong_sent:89
cluster_stats_messages_meet_sent:1
cluster_stats_messages_sent:194
cluster_stats_messages_ping_received:89
cluster_stats_messages_pong_received:105
cluster_stats_messages_received:194
------------------------------------------
还可以查看节点信息
执行:cluster nodes
如下:
-----------------------------------------
8141634b2b7eddc49494d9a6db1a54f355a2931a 172.17.0.6:6379@16379 slave 209df49459b4381369a6df9a1ec15cbad720569c 0 1594916370000 5 connected
8e4e787885fe0af1419b7c2ef9dadb83b76d579e 172.17.0.3:6379@16379 master - 0 1594916372700 2 connected 5461-10922
8982c04e80b04a312a241f9ae68539160dc90e61 172.17.0.5:6379@16379 slave b19e42dbf88969785e40784638b6386428c0d2b1 0 1594916371673 4 connected
b19e42dbf88969785e40784638b6386428c0d2b1 172.17.0.4:6379@16379 master - 0 1594916371000 3 connected 10923-16383
b4ecb477f53c59a3448286051778106e261ee3cf 172.17.0.7:6379@16379 myself,slave 8e4e787885fe0af1419b7c2ef9dadb83b76d579e 0 1594916369000 6 connected
209df49459b4381369a6df9a1ec15cbad720569c 172.17.0.2:6379@16379 master - 0 1594916371000 1 connected 0-5460
-----------------------------------------
下一步:使用集群
使用 redis-cli -c该命令进入即可,一定要有-c,否则报错。
127.0.0.1:6379> set a aaa
-> Redirected to slot [15495] located at 172.17.0.4:6379
OK
172.17.0.4:6379> set b bbb
-> Redirected to slot [3300] located at 172.17.0.2:6379
OK
172.17.0.2:6379> set c ccc
-> Redirected to slot [7365] located at 172.17.0.3:6379
OK
172.17.0.3:6379> set d ddd
-> Redirected to slot [11298] located at 172.17.0.4:6379
OK
172.17.0.4:6379> get a
ok。
redis
redis概述
nosql not only sql
redis高度缓存 mongo数据库
1/用redis做单点登录系统(分布式)
2/做数据库层的上一层高速缓存层(内存)
redis是基于内存的一种存储,使用key:value
redis3.0之后开始支持分布式
它支持存储的value类型相对更多:
包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)
字符串类型 string 512M
散列类型 hash 2^32-1
列表类型 list 2^32-1
集合类型 set 2^32-1
有序集合类型 zset
redis支持持久化
会生成一个文件,里面记录着所有的数据
redis默认端口:6379
redis数据存取
redis命令大全参考:
字符串类型:
set name zhangsan
get name
keys *
https://blog.csdn.net/qq_15071263/article/details/83576713
select 1选择数据库,模式使用0号数据库,共16个
del name
incr num自增(点赞效果)
decr num自减
append name2 xxxx
expire a 10(秒)
ttl a 查看剩余时间(-2表示该键被删除)
persist c让c的生存时间失效,不计时了。
其他数据类型请见文档。
springboot集成redis
1/导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2/配置 ip 端口
#redis
spring.redis.host=192.168.1.3
spring.redis.port=7006
3./使用redis
@RestController
public class RedisController {
@Autowired
StringRedisTemplate template;
@RequestMapping("redis")
public void test (){
//字符串
template.opsForValue();
//hash map
template.opsForHash();
//list
template.opsForList();
//set 无序 不重复
template.opsForSet();
//有序set
template.opsForZSet();
template.opsForValue().set("aaa","bbb");
String s = template.opsForValue().get("aaa");
System.out.println(s);
template.opsForValue().increment("age");
//关于key的操作都在template中
template.expire("aaa", Duration.ofSeconds(10));
//关于value都在ops中
Set<String> keys = template.keys("*");
for (String ss : keys) {
System.out.println(ss);
}
template.opsForHash().put("dog","name","ahuang");
template.opsForHash().put("dog","age","2");
System.out.println(template.opsForHash().get("dog","age"));
}
}
springboot集成redis集群版时,连接超时。
问题遗留。
redis单点登录
简单示例:(有点丑)
<div>
<input type="text" name="username" id="username">
<input type="text" name="password" id="password">
<button id="login">登录</button>
</div>
</body>
<script src="webjars/jquery/3.5.1/jquery.js"></script>
<script src="webjars/jquery-cookie/1.4.1/jquery.cookie.js"></script>
<script>
$("#login").click(function () {
// var token = $.cookie("token");
$.ajax({
url:"/login",
type:"post",
dataType:"json",
data:{
username:$("#username").val(),
password:$("#password").val()
// token:token
},
success:function (data) {
}
})
});
</script>
</html>
```java
@Controller
public class LoginController {
@Autowired
StringRedisTemplate template;
@ResponseBody
@RequestMapping("login")
public String login(String username, String password, HttpServletResponse response){
//我们不真的去查数据库,模拟一下
if (username.equals("张三")&&password.equals("123456")){
//生成token 还要将用户数据变成json
// commons-loging
String uuid = UUID.randomUUID().toString();
String s = RandomStringUtils.randomNumeric(10);
String token = uuid+s;
Cookie cookie = new Cookie("token",token);
response.addCookie(cookie);
//在redis(集群)里存一份
//k:v token:user
template.opsForHash().put(token,"username","张三");
template.opsForHash().put(token,"age","18");
template.expire(token, Duration.ofMinutes(30));
return "success";
}else {
return "error";
}
}
@RequestMapping("loginPage")
public String loginPage(){
return "login";
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是购物车
</body>
</html>
@Controller
public class CartController {
@Autowired
StringRedisTemplate template;
//过滤器filtter
@RequestMapping("cartPage")
public String cartPage(HttpServletRequest request){
//浏览器中所有的cookie
Cookie[] cookies = request.getCookies();
for (Cookie c : cookies) {
System.out.println(c.getName());
if (c.getName().equals("token")){
String token = c.getValue();
Boolean b = template.hasKey(token);
if (b){
return "cart";
}
}
}
return "login";
}
}
redis做缓存
数据库层之上,应该有一个redis层
只能放读的数据。
数据的特点:变化周期小,例如淘宝的商品详情数据。