Spring Cloud Alibaba2022之Sentinel学习
Sentinel介绍
Sentinel是一个面向云原生微服务的流量控制、熔断降级组件。
Sentinel 分为两个部分:
- 核心库:(Java 客户端)不依赖任何框架/库,能够运行于所有 Java运行时环
境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持3 - 控制台:(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需
要额外的 Tomcat 等应用容器
安装
Sentinel下载地址如下:
Sentinel控制台下载地址
下载后,进入保存目录,通过命令执行jar文件
java">java -jar sentinel-dashboard-1.8.7.jar
在浏览器输入http://localhost:8080/#/login进行访问,用户名和密码都是sentinel
使用
在需要使用Sentinel组件的服务的pom文件中加入Sentinel核心库的依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
启动服务,然后随便访问服务中的一个接口,这个接口会出现在控制台中。
如果不想访问接口,需要将eager设置为true
簇点链路:就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。流控、熔断等都是针对簇点链路中的资源来设置的。
下面的图显示了服务的调用链路。访问了/consumer/test/{userId}接口,然后远程调用了http://providerservice/user/{userId}接口,通过右边的操作可以进行限流、降级。
服务限流
Sentinel的流控模式有3种:
- 直接模式:对当前资源限流
- 关联模式:高优先级资源触发阈值,对低优先级资源限流
- 链路模式:阈值统计时,只统计从指定资源进入当前资源的请求,是对请求来源的限流
流控效果也有3钟:
- 快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式;
- warm up:也叫预热模式,是应对服务冷启动的一种方案;对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值;
- 排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长;
直接模式
给/consumer/test/{userId}接口设置的最大QPS为3,流控效果是快速失败,则这个接口每秒钟最多能处理3个请求,第4个请求会被拒绝并抛出异常
在Jmeter中创建测试计划进行测试,在1秒内:创建5个线程,这个5个线程只执行1次
通过结果可以到,前面3个线程的请求成功了,而后面两个线程的请求失败了
关联模式
统计与当前资源相关的另一个资源,达到阈值时,对当前资源限流。
使用场景:比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
总结一下使用关联模式的条件:
- 两个资源有竞争关系
- 一个资源优先级高,一个资源优先级低
链路模式
只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。
例如有两条请求链路:/consumer/{userId} —> /user/{userId} 和 /consumer/test/{userId} --> /user/{userId},现在只希望统计从/consumer/test/{userId}进入的请求,则可以像下面的图中这样配置:
sentinel默认只会标记controller中的方法为资源,如果要标记其它的方法,则需要使用@sentinelResource注解;Sentinel默认会将controller方法做整合,导致链路的流控模式失效,此时需要使用如下的配置进行处理:
spring:cloud:sentinel:web-context-unify: false #如果配置为 true,则对所有请求应用统一的流控和熔断规则;如果配置为 false,则对每个请求的上下文信息进行独立处理,并针对每个 URL 或资源配置流控和熔断规则
@SentinelResource注解使用
@SentinelResource的常用属性如下:
- value:资源名称
- blockHandler:Sentinel的限流处理方法
- fallback:代码执行出现异常的处理
- fallbackClass:全局统一处理方法,需要为静态函数不然无法解析
注意:当blockHandler和fallback同时使用时,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出
java"> @RequestMapping("/getDate")@SentinelResource(value = "getDate", blockHandler = "blockHandler")public String getDate() {String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern(pattern));System.out.println("result: " + format);return format;}/*** blockHandler方法的签名和getDate方法类似,但是要比getDate方法多一个参数BlockException* 这是必须的,不然不能正常执行这个方法**/public String blockHandler(BlockException e) {return "getDate request is blocking, please wait!";}
给这个资源配置限流规则
可以看到页面显示了blockHandler方法返回的内容
流控效果之warm up
warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor(预热时长)的默认值是3。
流控效果之排队等待
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常
服务隔离和降级
虽然限流可以尽量避免因高并发而引起的服务故障,但服务还可能会因为其它原因发生故障。而要讲这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。不管是服务隔离还是熔断降级都是对客户端(调用方)的保护。
OpenFeign整合Sentinel
使用sentinel进行流控后,如果流量过大,导致接口访问异常,此时应该对接口进行降级处理。如果项目中使用的是OpenFeign进行调用远程接口的话,此时需要进行整合从而实现降级处理。给Feign Client编写失败后的降级处理逻辑有两种方法:
- FallBackClass:无法对远程调用的异常进行处理;
- FallBackFactory:可以对远程调用的异常进行处理;
这里我们采用第二种,其实两种方式的使用方法都是差不多的。
首先需要开启整合
feign:sentinel:enabled: true
降级逻辑处理,这里只是简单地把异常信息返回了。
java">/*** @author * @version 1.0* @description: 对远程调用的异常进行处理* @date 2024/3/6 */
public class UserFallBack implements FallbackFactory<ProviderServiceClient> {private static final Logger LOGGER = LoggerFactory.getLogger(UserFallBack.class);@Overridepublic ProviderServiceClient create(Throwable cause) {LOGGER.error("error: " + cause.getMessage() + " " + cause.getCause());return new ProviderServiceClient() {@Overridepublic String getUserById(String userId) {return "远程服务调用异常" + cause.getMessage();}};}}
将UserFallBack注册为Bean
java"> @Beanpublic UserFallBack userFallBack() {return new UserFallBack();}
通过@FeignClient注解的fallbackFactory 属性指定发送异常时进行处理的类
java">@FeignClient(value = "providerservice", contextId = "providerservice", fallbackFactory = UserFallBack.class) //指定要调用的服务
public interface ProviderServiceClient {//要调用的服务中的请求,需要被调用的服务中的请求方法保持一致@GetMapping("/user/{userId}")public String getUserById(@PathVariable("userId") String userId);}
最后,如果调用此接口发生异常就会走UserFallBack中的create方法进行处理。
线程隔离
熔断降级
熔断降级是解决雪崩问题的重要手段,其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。具体流程如下图所示:
在Sentinel中,断路器的熔断策略有3种:
-
慢异常:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断
RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。 -
异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。
统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
授权规则
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。
- 白名单:来源在白名单内的请求允许访问
- 黑名单:来源在黑名单内容的请求不允许访问
参考
- 微服务系列:Sentinel 之 @SentinelResource 注解配置