SpringCloud的学习笔记
每周一句:万物自有节奏,忙有价值,闲有滋味
1.openFegin
-
使用步骤
-
引入依赖
<!--openFeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--负载均衡器--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> -
在启动类上加入注解:@EnableFeignClients
-
创建接口
@FeignClient("item-service") // 要掉用的服务名 public interface ItemClient { @GetMapping("/items") public List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids); } -
使用:直接注入到要用的类中
@Autowired private final ItemClient itemClient;
-
-
连接池
-
引入依赖
<!--OK http 的依赖 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency> -
开启连接池
feign: okhttp: enabled: true # 开启OKHttp功能
-
-
推荐配置
-
新建模块用于存放要暴露的接口,将client和要使用的实体类放进来
-
在要用的模块中引入
-
在启动类上扫描路径
@EnableFeignClients(basePackages = "com.hmall.api.cilent")
-
-
日志
-
级别:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
-
配置
-
创建配置类
public class DefaultFeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } -
全局配置
@EnableFeignClients(basePackages = "com.hmall.api.cilent", defaultConfiguration = DefaultFeignConfig.class) -
效果
12:42:10:381 DEBUG 60301 --- [nio-8082-exec-1] c.h.cart.mapper.CartMapper.selectList : ==> Preparing: SELECT id,user_id,item_id,num,name,spec,price,image,create_time,update_time FROM cart WHERE (user_id = ?) 12:42:10:394 DEBUG 60301 --- [nio-8082-exec-1] c.h.cart.mapper.CartMapper.selectList : ==> Parameters: 1(Long) 12:42:10:408 DEBUG 60301 --- [nio-8082-exec-1] c.h.cart.mapper.CartMapper.selectList : <== Total: 1 12:42:10:433 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] ---> GET http://item-service/items?ids=100000006163 HTTP/1.1 12:42:10:433 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] ---> END HTTP (0-byte body) 12:42:10:525 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] <--- HTTP/1.1 200 (92ms) 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] connection: keep-alive 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] content-type: application/json 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] date: Sat, 10 Aug 2024 04:42:10 GMT 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] keep-alive: timeout=60 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] transfer-encoding: chunked 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] [{"id":"100000006163","name":"巴布豆(BOBDOG)柔薄悦动婴儿拉拉裤XXL码80片(15kg以上)","price":67100,"stock":10000,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t23998/350/2363990466/222391/a6e9581d/5b7cba5bN0c18fb4f.jpg!q70.jpg.webp","category":"拉拉裤","brand":"巴布豆","spec":"{}","sold":11,"commentCount":33343434,"isAD":false,"status":2}] 12:42:10:526 DEBUG 60301 --- [nio-8082-exec-1] com.hmall.api.cilent.ItemClient : [ItemClient#queryItemByIds] <--- END HTTP (371-byte body)
-
-
2.Gateway
-
使用
-
引入pom
<!--网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--nacos discovery--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--负载均衡--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> -
配置yaml
server: port: 8080 spring: application: name: gateway cloud: nacos: server-addr: 127.0.0.1:8848 gateway: routes: - id: item # 路由规则id,自定义,唯一 uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表 predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务 - Path=/items/**,/search/** # 这里是以请求路径作为判断规则
-
-
自定义过滤器
-
GatewayFilter:路由过滤器,作用于任意指定的路由;默认不生效,要配置到路由后生效
-
GlobalFilter:全局过滤器,作用范围是所有的路由;声明后自动动生效
-
代码
/** * @author Zzhaozhao * @date 2024/8/11 * @description 过滤器 * @apiNote */ @Component public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); System.out.println(headers); return chain.filter(exchange); } @Override public int getOrder() { return 0; } } /** * @author Zzhaozhao * @date 2024/8/11 * @description 过滤器 * @apiNote */ @Component public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> { @Override public GatewayFilter apply(Config config) { return new OrderedGatewayFilter((exchange, chain) -> { System.out.println(config.getA()); return chain.filter(exchange); },1); }@Data public static class Config{ private String A; private String B; private String C; } @Override public List<String> shortcutFieldOrder() { return List.of("a","b","c"); } public MyGatewayFilterFactory(){ super(Config.class); } }
-
3.登陆校验拦截器
-
在gateway模块添加拦截器
@Component @RequiredArgsConstructor public class AuthGlobalFilter implements GlobalFilter, Ordered { private final JwtTool jwtTool; private final AuthProperties authProperties; private final AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1.获取请求路径 ServerHttpRequest request = exchange.getRequest(); // 2.判断是否需要拦截 if (isFilter(request.getPath().toString())) { return chain.filter(exchange); } // 3.获取token HttpHeaders headers = request.getHeaders(); String token = null; List<String> list = headers.get("authorization"); if (!CollectionUtils.isEmpty(list)) { token = list.get(0); } // 4.校验token Long userId = null; try { userId = jwtTool.parseToken(token); } catch (UnauthorizedException e) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } // 5.获取用户信息传递下去 String user = userId.toString(); exchange.mutate() .request(builder -> builder.header("user-info", user)) .build(); return chain.filter(exchange); } /** * 判断是否为过滤的地址 * * @param path * @return */ private boolean isFilter(String path) { for (String excludePath : authProperties.getExcludePaths()) { if (antPathMatcher.match(excludePath, path)) { return true; } } return false; } @Override public int getOrder() { return 0; } } -
配置白名单
hm: jwt: location: classpath:hmall.jks alias: hmall password: hmall123 tokenTTL: 30m auth: excludePaths: - /search/** - /users/login - /items/** - /hi
4.微服务获取用户
-
获取存入网关请求里面的用户信息通过拦截
public class UserInfoInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String userInfo = request.getHeader("user-info"); if(StrUtil.isNotBlank(userInfo)){ UserContext.setUser(Long.parseLong(userInfo)); } return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { UserContext.removeUser(); } } -
编写配置类加载拦截器
@Configuration @ConditionalOnClass(DispatcherServlet.class) public class MvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserInfoInterceptor()); } } -
要注意的是,这个配置类默认是不会生效的,因为它所在的包是
com.hmall.common.config,与其它微服务的扫描包不一致,无法被扫描到,因此无法生效。基于SpringBoot的自动装配原理,我们要将其添加到
resources目录下的META-INF/spring.factories文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.hmall.common.config.MyBatisConfig,\ com.hmall.common.config.JsonConfig,\ com.hmall.common.config.MvcConfig -
在openfeign中创建bean将用户信息放入请求头中
@Bean public RequestInterceptor userInfoInterceptor(){ return requestTemplate -> { String user = UserContext.getUser().toString(); if(StrUtil.isNotBlank(user)){ requestTemplate.header("user-info",user); } }; }
5.配置管理
-
配置共享:抽取公公共配置到nacos
spring: datasource: url: jdbc:mysql://${hm.db.host:127.0.0.1}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver username: ${hm.db.un:root} password: ${hm.db.pw:123} mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler global-config: db-config: update-strategy: not_null id-type: auto -
引入pom
<!--nacos配置管理--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--读取bootstrap文件--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> -
创建bootstrap.yaml文件
spring: application: name: cart-service # 服务名称 profiles: active: dev cloud: nacos: server-addr: 127.0.0.1:8848 # nacos地址 config: file-extension: yaml # 文件后缀名 shared-configs: # 共享配置 - dataId: shared-jdbc.yaml # 共享mybatis配置 - dataId: shared-log.yaml # 共享日志配置 - dataId: shared-swagger.yaml # 共享日志配置 -
修改application.yaml的配置
server: port: 8082 feign: okhttp: enabled: true # 开启OKHttp连接池支持 hm: swagger: title: 购物车服务接口文档 package: com.hmall.cart.controller db: database: hm-cart
6.配置热更新
-
创建配置类
@Data @Component @ConfigurationProperties(prefix = "hm.cart") public class CartProperties { private Integer maxCartCount; } -
nacos创建配置,命名规则为服务名-环境.后缀
hm: cart: maxCartCount: 10 # 购物车商品数量上限
7.雪崩问题
-
原因
- 微服务相互调用,服务提供者出现故障或阻塞
- 服务调用者没有做好异常处理,导致自身故障
- 调用链中的所有服务级联失败,导致整个集群故障
-
解决思路
-
尽量避免服务出现阻塞或故障
- 保障代码的健壮性
- 保证网络畅通
- 能应对较高的并发
-
服务调用者做好远程调用异常的后备方案,避免故障扩散
-
-
解决方案
- 请求限流:限制访问微服务的请求的并发量,避免服务因流量激增出现故障
- 线程隔离:也叫舱壁模式,模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散。但是还是会有很多的请求进行访问故障的线程也会造成服务阻塞,那么就需要服务熔断
- 服务熔断:由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,则拦截该接口的请求,熔断期间,所有请求快速失败,全走回掉函数
sentinel
-
使用
-
下载jarjar
-
控制台输入命令
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar -
在项目的pom文件中引入依赖
<!--sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> -
在yaml中引入
spring: cloud: sentinel: transport: dashboard: localhost:8090 http-method-specify: true # 开启请求方式前缀
-
-
请求限流
- 在簇点链路后面点击流控按钮,即可对其做限流配置