一、Sentinel简介
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
1、sentinel的特征
- 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
-完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等
2、sentinel的主要特性
sentinel的开源生态
sentinel 的架构图
3、sentinel的主要优势和特性
- 轻量级,核心库无多余依赖,性能损耗小
- 方便接入,开源生态广泛
- 丰富的流量控制场景
- 易用的控制台,提供实时监控、机器发现、规则管理等能力
- 完善的扩展性设计,提供多样化的 SPI 接口,方便用户根据需求给 Sentinel 添加自定义的逻辑
4、sentinel与spring cloud Hystrix 对比
Sentinel | Hystrix | |
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
5、sentinel分为两个部分
- 核心库(Java 客户端): 不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持
- 控制台(Dashboard): 基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器
二、Sentinel安装与使用
1、Sentinel控制台的下载
下载地址:https://github.com/alibaba/Sentinel/releases/tag/1.8.3
我们选择1.8.3版本,直接下载jar包
放到指定文件夹中
2、Sentinel控制台的启动
cmd 启动jar包
java -jar sentinel-dashboard-1.8.3.jar
2、访问浏览器输入:localhost:8080
账号密码 默认都是 sentinel
3、为服务打开sentinel的监控
引入sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
手把手教你搭建springcloud alibaba微服务–openfeign
yml文件添加sentinel相关配置
server:
port: 9090
spring:
application:
name: mdx-shop-user
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: mdx
group: mdx
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
feign:
sentinel:
enabled: true
启动user服务并访问user服务的测试接口
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("getOrderNo")
public String getOrderNo(String userId,String tenantId,HttpServletRequest request){
return userService.getOrderNo(userId,tenantId,request);
}
}
浏览器访问
http://localhost:9090/user/getOrderNo?userId=mdx123456&tenantId=123
然后查看sentinel控制台发现已经监控到了我们服务的接口
三、Sentinel的控制规则
1、流控规则
1.1、流控规则的各个属性
- 资源名: 唯一名称,默认请求路径,表示对该资源进行流控
- 针对来源: Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单击阈值:
QPS:(每秒钟的请求数量):当调用该api的QPS达到阈值时,进行限流
线程数:当调用该线程数达到阈值的时候,进行限流 - 是否集群:不需要集群
- 流控模式:
直接: api达到限流条件时,直接限流
关联: 当关联的资源达到阈值时,就限流自己
链路: 只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】 - 流控效果:
快速失败: 直接失败,抛异常
Warm Up: 根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFctor,经过预热时长,才达到设置的QPS阈值
排队等待: 匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
1.2、新增流控规则
资源名称就是我们的接口访问路径
然后我们一秒一次访问一下接口(正常)
http://localhost:9090/user/getOrderNo?userId=mdx123456&tenantId=123 可以看到正常返回数据
接下来我们快速请求接口,一秒钟点击多次
发现已经被限流了
1.3、流控规则–关联流控模式
我们模拟一下流控模式中的关联模式
关联: 当关联的资源达到阈值时,就限流自己,也就是说关联的资源(接口),QPS为1时,一秒内被多次请求的时候,自己的接口就会被限流。
我们在user服务中创建一个关联接口 /user/sentinelB
@GetMapping("sentinelB")
public String sentinelB(){
return "我是关联接口";
}
然后修改一下流控规则
我们用jmeter模拟一下请求sentinelB接口
一秒3个请求并循环
在这期间我们访问/user/getOrderNo接口
发现被限流了
2、@SentinelResource注解的使用
2.1、@SentinelResource的属性
- value:作用指定资源名称,必填
- entryType:entry类型,标记流量的方向,指明是出口流量,还是入口流量;取值 IN/OUT ,默认是OUT。非必填
- blockHandler:处理BlockException的函数名称,函数要求为
必须是public
返回类型与原方法一致
参数类型需要和原方法相匹配,并在最后加上BlockException类型的参数
默认需和原方法在同一个类中,如果希望使用其他类的函数,可配置blockHandlerClass,并指定blockHandlerClass里面的方法 - blockHandlerClass:存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析。函数要求为:
必须是public
返回类型与原方法一致
参数类型需要和原方法相匹配,并在最后加上BlockException类型的参数 - fallback:用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了execptionsToIgnore 里面排除掉的异常类型)进行处理,函数要求为:
返回类型与原方法一致
参数类型需要和原方法相匹配,Sentinel 1.6版本之后,也可在方法最后加上Throwable类型的参数
默认需和原方法在同一个类中,若希望使用其他类的函数,可配置fallbackClass,并指定fallbackClass里面的方法 - fallbackClass:存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。
- defaultFallback:用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:
返回类型与原方法一致
方法参数列表为空,或者有一个Throwable类型的参数
默认需要和原方法在同一个类中,若希望使用其他类的函数,可配置fallbackclass,并指定fallbackClass里面的方法。 - exceptionsToIgnore:指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出
- exceptionsToTrace:需要trace的异常
2.2、按照资源名称的方式限流,并对限流流进行友好处理
我们修改一下/user/getOrderNo接口的代码,增加@SentinelResource注解,并做后续处理
演示代码:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("getOrderNo")
@SentinelResource(value = "getOrderNoResource",blockHandler = "getOrderNoBlockHandler",blockHandlerClass = UserController.class)
public String getOrderNo(String userId, String tenantId, HttpServletRequest request){
return userService.getOrderNo(userId,tenantId,request);
}
/**
* 限流后续操作方法
* @param e
* @return
*/
public static String getOrderNoBlockHandler(String userId, String tenantId, HttpServletRequest request,BlockException e){
String msg = "不好意思,前方拥挤,请您稍后再试";
return msg;
}
}
其中 getOrderNoResource 为资源名称,getOrderNoBlockHandler 为兜底方法的方法名称,blockHandlerClass = UserController.class 这个是兜底方法所在的类, 与资源方法在同一个类中 这属性可以省去,这里只是演示
注意:返回类型与原方法一致,参数类型需要和原方法相匹配,并在最后加上BlockException类型的参数
然后再修改一下流控规则,把资源名称换成我们注解中的 getOrderNoResource
快速访问我们的接口,http://localhost:9090/user/getOrderNo?userId=mdx123456&tenantId=123
发现返回的信息已经变成我们兜底方法自定义的返回提示了
3、熔断规则
3.1、熔断规则的几种策略
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断
- 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%
- 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断
3.2、新建一个熔断规则
我们先写一个新的接口并且睡眠一秒,使用注解资源名称的形式,并创建自定义兜底方法
方法接口地址 /user/sentinelA
@GetMapping("/sentinelA")
@SentinelResource(value = "sentinelAResource" , fallback = "sentinelAResource", fallbackClass = UserSentinelResourceHandler.class)
public String sentinelA(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是sentinelA");
return "我是sentinelA";
}
创建单独的handler类来处理兜底方法
@Component
public class UserSentinelResourceHandler {
public static String sentinelAResource(Throwable throwable){
System.out.println("触发熔断,服务不可用");
return "触发熔断,服务不可用";
}
}
接着我们在控制台新建一个熔断规则(使用慢调用比例的策略)
我们对以上添加的熔断规则解释一下:
在 2 秒的统计时间(统计时长)内,对资源接口的请求数大于 5 (最小请求数),请求响应时间(最大 RT)大于200毫秒的请求占比超过比例阈值 0.1 ,则下一个请求开始触发熔断,熔断时长是 5 秒,也就是 5 秒内,熔断器是打开状态,这期间的请求都会触发熔断,5秒过后熔断器关闭,此时熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
RT(平均响应时间): 当1s内持续进入5个请求,且对应请求的平均响应时间(秒级)均超过阈值,那么在接下来的时间窗口期内,对该方法的调用都会自动的熔断。注意Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要更改上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置
然后我们测试一下
模拟下并发,一秒十个请求循环调用
我们发现触发阈值之后 已经触发了熔断机制,如下
3、热点规则
热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的数据,并对其访问进行限制,比如对某个商品id进行限制,或者对某个用户id进行限制
3.1、热点规则属性
参数索引:方法中参数的索引第几个参数
单机阈值:每秒达到单机阈值的数量就会触发兜底方法
4.1、新建一个热点规则
我们先创建一个测试方法,使用注解资源名称的形式,并创建自定义兜底方法
/**
* 测试centinel热点规则限流
* @param userId
* @param shopId
* @return
*/
@GetMapping("/hotspot")
@SentinelResource(value = "hotspotResource" , blockHandler = "hotspotResource", blockHandlerClass = UserSentinelResourceHandler.class)
public String hotspot(@RequestParam(value = "userId" ,required = false) String userId,
@RequestParam(value = "shopId" ,required = false) String shopId){
System.out.println("我是hotspot");
return "我是hotspot";
}
兜底方法
public static String hotspotResource(String userId, String shopId,BlockException blockException){
System.out.println("您被认为恶意访问,触发热点限流");
return "您被认为恶意访问,触发热点限流";
}
然后我们在控制台新建热点规则
其中参数索引 0 代表的就是userId这个参数
我们访问一下接口
先访问带shopId的(一秒内多次访问) http://localhost:9090/user/hotspot?shopId=4
正常返回
再访问下带userId参数的(一秒内多次访问) http://localhost:9090/user/hotspot?userId=4
返回降级函数的内容
4、系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
如果配置了系统规则,所有的接口都会被限流
这里系统规则就不演示了,关于sentinel还有很多规则,策略和属性,大家有兴趣可以自行研究下。
关于sentinel的使用篇就到这里了。