微服务保护
微服务保护
一、微服务保护基础
1.1 雪崩问题的产生原因
在前面两篇blog中解决了微服务项目中远程调用、网关和配置相关的内容,这已经可以解决微服务项目中大部分的问题了,但是仍存在一些问题,考虑如下场景:
一个微服务项目,服务A需要调用服务B提供的服务,但是服务B可能会抛出异常;或者服务B并发量很高,导致服务B响应服务A的时延很长,甚至超时导致查询失败。此时会导致服务A的响应时间很长,若在高并发的场景下,Tomcat连接占用很多,会拖垮服务中其他接口的响应时间。
若还有其他服务调用服务A,同样会拖垮调用者服务,这样会导致级联失败,产生雪崩问题。
1.2 服务保护方案
- 请求限流
- 线程隔离
- 服务熔断
请求限流
产生雪崩问题的原因,归根结底就是并发太高!因此请求限流,就是限制或控制接口访问的并发流量,避免服务因流量激增而出现故障。
线程隔离
当一个服务接口响应时间长,并且处在高并发状态时,此时会占用大量的Tomcat连接,这样会导致服务内其他接口的请求时延也大大增加。为解决这个问题,可以对服务内的接口设置占用最大线程数,这样该服务接口不会大量占用Tomcat连接,可以保护服务内的其他接口。
服务熔断
限流和线程隔离策略可以达到对服务内其他接口的保护,但是这种处理策略会让服务调用处堆积大量的请求。
服务熔断策略中存在一个熔断器,它会统计服务调用的时延和异常次数,若时延过高的响应或者异常次数超过阈值,那么就会中断被请求服务的接口,所有请求走fallback逻辑。
在这个策略中,需要做两件事:
- 监控服务提供接口的异常率或者时延过高的响应;
- 便也fallback处理逻辑,一般是返回默认值或者提示
二、Sentinel
2.1 介绍
Sentinel是SpringCloudAlibaba提供的一款服务保护框架(https://sentinelguard.io/zh-cn/docs/introduction.html)。
下载好后,通过如下命令就可以运行
1 | java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar |
2.2 快速使用
- 步骤一:引入sentinel依赖
1
2
3
4
5<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency> - 步骤二:添加sentinel board地址配置
1
2
3
4
5
6spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true # 开启请求方式前缀 - 步骤三:OpenFeign整合sentinel
1
2
3feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
完成以上配置,接下载只需要在sentinel网站上“点点点”就可以实现请求限流和线程隔离啦~
2.3 线程熔断实现
在1.3小节中提到过,要实现线程熔断需要完成两件事:监控和编写fallback逻辑代码。在sentinel中只提供了监控功能,剩下的fallback代码需要自己编写。
步骤一:在api模块中编写实现
FallbackFactory
接口的实现类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ItemClientFallback implements FallbackFactory<ItemClient> {
public ItemClient create(Throwable cause) {
return new ItemClient() {
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);
// 查询购物车允许失败,查询失败,返回空集合
return CollUtils.emptyList();
}
public void deductStock(List<OrderDetailDTO> items) {
// 库存扣减业务需要触发事务回滚,查询失败,抛出异常
throw new BizIllegalException(cause);
}
};
}
}步骤二:在配置类中将刚刚编写的fallback注册为一个bean
1
2
3
4
public ItemClientFallback itemClientFallback () {
return new ItemClientFallback();
}步骤三:在相应的Client类中的FeignClient注解中配置fallbackFactory属性
1
2
3
4
public interface ItemClient步骤四:在sentinel中配置熔断规则