1. Sentinel 控制台介绍
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则 管理和推送的功能。
Sentinel 控制台包含如下功能:
-
**查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。 **
- **监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信 息,最终可以实现秒级的实时监控。 **
-
**规则管理和推送:统一管理推送规则。 **
- 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。
阿里云提供了 企业级的 Sentinel 控制台,应用高可用服务 AHAS
- ** 实时监控 监控接口的通过的 QPS 和拒绝的 QPS**
- ** 簇点链路 用来显示微服务的所监控的 API **
- 流控规则
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流 量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。 同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流 或者所有规则遍历完毕。一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限 流效果。
Field 说明 默认值
resource 资源名,资源名是限流规则的作用对象
count 限流阈值
限流阈值类型
流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS。类型由 FlowRule 的 grade 字段来定义。其中,0 代表根据并发数量来限流,1 代表根据 QPS 来进行流量控制。
QPS(Query Per Second):每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。
进入簇点链路选择具体的访问的 API,然后点击流控按钮
测试:http://localhost:8800/user/findOrderByUserId/1
BlockException 异常统一处理
springwebmvc 接口资源限流入口在 HandlerInterceptor 的实现类 AbstractSentinelInterceptor 的 preHandle 方法中,对异常的处理是 BlockExceptionHandler 的实现类
sentinel 1.7.1 引 入 了 sent i nel -spri ng-webmvc-adapt er. j ar
自定义 BlockExceptionHandler 的实现类统一处理 BlockException
@Slf4j
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throw
Exception {
log.info("BlockExceptionHandler BlockException================"+e.getRule()); 7
R r = null;
if (e instanceof FlowException) {
= R.error(100,"接口限流了");
else if (e instanceof DegradeException) {
= R.error(101,"服务降级了");
else if (e instanceof ParamFlowException) {
= R.error(102,"热点参数限流了");
else if (e instanceof SystemBlockException) {
= R.error(103,"触发系统保护规则了");
else if (e instanceof AuthorityException) {
= R.error(104,"授权规则不通过");
26 //返回json数据
27 response.setStatus(500);
28 response.setCharacterEncoding("utf‐8");
29 response.setContentType(MediaType.APPLICATION_JSON_VALUE);
30 new ObjectMapper().writeValue(response.getWriter(), r);
31
32 }
33 }
测试:
并发线程数
并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务 不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致 线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程 池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程 数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控
制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超 出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。
可以利用 jmeter 测试
流控模式
基于调用关系的流量控制。调用关系包括调用方、被调用方;一个方法可能会调用其它方法,形成一个调 用链路的层次关系。
直接
资源调用达到设置的阈值后直接被流控抛出异常
关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段 的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读 写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资 源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。
链路
根据调用链路入口限流。
NodeSelectorSlot 中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵 树的根节点是一个名字为 machine-root 的虚拟节点,调用链的入口都是这个虚节点的子节点。 一棵典型的调用树如下图所示:
上图中来自入口 Entrance1 和 Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计 信息对资源限流。
测试会发现链路规则不生效
注意,高版本此功能直接使用不生效,如何解决?
从 1.6.3 版本开始,Sentinel Web filter 默认收敛所有 URL 的入口 context,导致链路限流不生效。 从1.7.0 版本开始,官方在 CommonFilter 引入了 WEB_CONTEXT_UNIFY 参数,用于控制是否收敛 context,将其配置 为 false 即可根据不同的 URL 进行链路限流。
- 需要引入 sentinelWebservlet 依赖
1 <!‐‐‐ 解决流控链路不生效的问题‐‐>
1
1 com.alibaba.csp
1 sentinel‐Web‐servlet
1
添加配置类,配置 CommonFilter 过滤器,指定 WEB_CONTEXT_UNIFY=false,禁止收敛 URL 的入口 context
1 public class SentinelConfig {
1 @Bean
1 public FilterRegistrationBean sentinelFilterRegistration() {
1 FilterRegistrationBean registration = new FilterRegistrationBean();
1 registration.setFilter(new CommonFilter());
1 registration.addUrlPatterns("/\*");
1 // 入口资源关闭聚合 解决流控链路不生效的问题
1 registration.addInitParameter(CommonFilter.WEB\_CONTEXT\_UNIFY, "false");
1 registration.setName("sentinelFilter");
1 registration.setOrder(1);
1 return registration;
1 }
14 }
再次测试链路规则,链路规则生效,但是出现异常
控制台打印 FlowException 异常
原因分析:
- Sentinel 流控规则的处理核心是 FlowSlot, 对 getUser 资源进行了限流保护,当请求 QPS 超过阈值 2 的时 候,就会触发流控规则抛出 FlowException 异常
- 对 getUser 资源保护的方式是 @SentinelResource 注解模式,会在对应的 SentinelResourceAspect 切面逻辑中处理 BlockException 类型的 FlowException 异常 (解决方案: 在 @SentinelResource 注解中指定 blockHandler 处理 BlockException)
@Override
@SentinelResource(value = "getUser",blockHandler = "handleException")
public UserEntity getUser(int id){
UserEntity user = baseMapper.selectById(id);
return user;
public UserEntity handleException(int id, BlockException ex) { UserEntity userEntity = new UserEntity(); userEntity.setUsername("===被限流降级啦===");
return userEntity;
}
如果此过程没有处理 FlowException, AOP 就会对异常进行处理,核心代码在 CglibAopProxy.CglibMethodInvocation#proceed 中,抛出 UndeclaredThrowableException 异常,属 于 RuntimeException
3.异常继续向上抛出,引入 CommonFilter 后,CommonFilter 添加了对异常的处理机制,所以会在 CommonFilter 中进行处理。 (注意此处对 BlockException 异常的处理是 UrlBlaockHandler 的实现类,而在 AbstractSentinelInterceptor 拦截器中是使用 BlockExceptionHandler 的实现类处理)
会抛出一个 RuntimeException 类型的 UndeclaredThrowableException 异常,然后打印到控制台 显示
此处又是有坑: FlowException 不会被 BlockException 异常机制处理,因为 FlowException 已经被封装为
RuntimeException 类型的 UndeclaredThrowableException 异常
测试:
自定义 CommonFilter 对 BlockException 异常处理逻辑,用于处理经过 CommonFilter 处理的 Spring webmvc 接口的 BlockException
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new CommonFilter()); registration.addUrlPatterns("/*");
// 入口资源关闭聚合 解决流控链路不生效的问题 registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false"); registration.setName("sentinelFilter");
registration.setOrder(1); //CommonFilter的BlockException自定义处理逻辑
WebCallbackManager.setUrlBlockHandler(new MyUrlBlockHandler());
return registration;
}
// UrlBlockHandler的实现类
@Slf4j
public class MyUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) thr
ows IOException {
23 log.info("UrlBlockHandler BlockException================"+e.getRule()); 24
25 R r = null;
26
27 if (e instanceof FlowException) {
= R.error(100,"接口限流了");
else if (e instanceof DegradeException) {
= R.error(101,"服务降级了");
else if (e instanceof ParamFlowException) {
= R.error(102,"热点参数限流了");
else if (e instanceof SystemBlockException) {
= R.error(103,"触发系统保护规则了");
else if (e instanceof AuthorityException) {
= R.error(104,"授权规则不通过");
43 //返回json数据
44 response.setStatus(500);
45 response.setCharacterEncoding("utf‐8");
46 response.setContentType(MediaType.APPLICATION_JSON_VALUE);
测试,此场景拦截不到 BlockException,对应 @SentinelResource 指定的资源必须在 @SentinelResource 注解中指定 blockHandler 处理 BlockException
总结: 为了解决链路规则引入 ComonFilter 的方式,除了此处问题,还会导致更多的问题,不建议使用 ComonFilter 的方 式。 流控链路模式的问题等待官方后续修复,或者使用 AHAS。
流控效果
当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:快速失败(直接 拒绝)、Warm Up(预热)、匀速排队(排队等待)。对应 FlowRule 中的 controlBehavior 字段。
快速失败
(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当 QPS 超过任意规则的阈值后,新 的请求就会被立即拒绝,拒绝方式为抛出 FlowException。这种方式适用于对系统处理能力确切已知的情况 下,比如通过压测确定了系统的准确水位时。
Warm Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情 况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓 慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
冷加载因子: codeFactor 默认是 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示
测试用例
@RequestMapping("/test")
2 public String test() {
3 try{
4 Thread.sleep(100);
5 } catch (InterruptedException e) {
6 e.printStackTrace();
7}
8 return "========test()========"; 9}
编辑流控规则
jmeter 测试
查看实时监控,可以看到通过 QPS 存在缓慢增加的过程
匀速排队
匀速排队(<span class="ne-text">RuleConstant.CONTROL\_BEHAVIOR\_RATE\_LIMITER</span>
)方式会严格控制请求通过的间隔时 间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
该方式的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求 到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不
是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
jemeter 压测
查看实时监控,可以看到通过 QPS 为 5,体现了匀速排队效果
降级规则
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。我们 需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪 崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
熔断降级规则说明
熔断降级规则(DegradeRule)包含下面几个重要的属性:
Field 说明
resource 资源名,即规则的作用对象
grade 熔断策略,支持慢调用比例/异常比例/异常数策略
count 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow 熔断时长,单位为 s
minRequestAmount 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)
statIntervalMs 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) slowRatioThreshold 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
熔断策略
慢调用比例
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的 响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大 于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔 断时长后熔断器会进入探测恢复状态(HALFOPEN 状态),若接下来的一个请求响应时间小于设置的慢 调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
测试用例
1 @RequestMapping("/test")
1 public String test() {
1 try {
1 Thread.sleep(100);
1 } catch (InterruptedException e) {
1 e.printStackTrace();
1 }
1 return "========test()========";
1 }
jemeter 压测/test 接口,保证每秒请求数超过配置的最小请求数
查看实时监控,可以看到断路器熔断效果
此时浏览器访问会出现服务降级结果
异常比例
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并 且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢 复状态(HALFOPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔 断。异常比率的阈值范围是 [0.0, 1.0],代表 0% 100%。
测试用例
@RequestMapping("/test")
2 public String test() {
3 try{
4 Thread.sleep(100);
5 } catch (InterruptedException e) {
6 e.printStackTrace();
7}
8 return "========test()========"; 9}
1 配置降级规则
查看实时监控,可以看到断路器熔断效果
异常数
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后 熔断器会进入探测恢复状态(HALFOPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔 断,否则会再次被熔断。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。 配置降级规则
jemeter 测试
查看实时监控,可以看到断路器熔断效果
热点参数限流
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据, 并对其访问进行限制。比如:
- **商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制 **
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用 进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
注意:
- 热点规则需要使用 @SentinelResource("resourceName")注解,否则不生效
- 参数必须是 7 种基本数据类型才会生效
测试用例
@RequestMapping("/info/{id}")
2 @SentinelResource(value = "userinfo",
3 blockHandlerClass = CommonBlockHandler.class,
4 blockHandler = "handleException2",
5 fallbackClass = CommonFallback.class,
6 fallback = "fallback"
7)
8 public R info(@PathVariable("id") Integer id){ 9 UserEntity user = userService.getById(id);
10 return R.ok().put("user", user);
11 }
配置热点参数规则
注意: 资源名必须是 @SentinelResource(value="资源名")中 配置的资源名,热点规则依赖于注解
具体到参数值限流,配置参数值为 3,限流阈值为 1
测试:
http://localhost:8800/user/info/1 限流的阈值为 3 http://localhost:8800/user/info/3 限流的阈值为 1
系统规则
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平 均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系 统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
-
**Load 自适应(仅对 Linux/Unixlike 机器生效):系统的 load1 作为启发指标,进行自适应系统 保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系 统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。 **
- **CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0 1.0),比较灵敏。 **
-
**平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。 **
- **并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。 **
-
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
编写系统规则
jemeter 配置
测试结果
授权控制规则
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控 制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名 单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求 通过。
来源访问控制规则(AuthorityRule)非常简单,主要有以下配置项:
-
**resource:资源名,即限流规则的作用对象。 **
- **limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB。 **
-
strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名
单模式。 配置授权规则
第一步:实现 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser 接口, 在 parseOrigin 方法中区分来源,并交给 Spring 管理
注意:如果引入 CommonFilter,此处会多出一个
10 public class MyRequestOriginParser implements RequestOriginParser {
11 /\*\*
11 \* 通过request获取来源标识,交给授权规则进行匹配
11 \* @param request
11 \* @return
11 \*/
11 @Override
11 public String parseOrigin(HttpServletRequest request) {
11 // 标识字段名称可以自定义
11 String origin = request.getParameter("serviceName");
20 // if (StringUtil.isBlank(origin)){
21 // throw new IllegalArgumentException("serviceName参数未指定");
22 // }
23 return origin;
23 }
25 }
测试:origin 是 order 的请求不通过。