抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Sentinel 使用

SentinelResource注解

在具体配置之前介绍一下一个非常重要的注解:@SentinelResource

注意该注解只能作用在实现类,不能作用在接口上面

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。

SentinelResource属性

@SentinelResource 注解包含以下属性:

value

​ 资源名称,必需项,因为需要通过resource name找到对应的规则,这个是必须配置的。

​ 定义资源名,该名称将会显示在控制台中,并且在定义流控以及熔断降级规则时,指定资源名称。

entryType

​ 入口类型,可选项: EntryType.IN和EntryType.OUT(默认为 EntryType.OUT)

1
2
3
4
5
public enum EntryType {
IN("IN"),
OUT("OUT");
}

blockHandler

​ 对应处理 BlockException 的函数名称,可选项。若未配置,则将 BlockException 直接抛出。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。

​ blockHandler是异常处理的方法,默认需要与原方法在同一个类中,如果需要在另外的类中定义,则需要设置blockHandlerClass,并且噶异常处理的方法应为静态方法。

blockHandlerClass

​ blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

fallback

​ 函数名称,可选项,仅针对降级功能生效(DegradeException)。fallback 函数的访问范围需要是 public,参数类型和返回类型都需要与原方法相匹配,并且需要和原方法在同一个类中。业务异常不会进入 fallback 逻辑。

fallbackClass

​ fallbackClass的应用和blockHandlerClass类似,fallback 函数默认需要和原方法在同一个类中。
​ 若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

defaultFallback(since 1.6.0)

​ 如果没有配置defaultFallback方法,默认都会走到这里来,默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑,默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理,若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。

exceptionsToIgnore(since 1.6.0)

​ 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

配置限流

流控模式

  • 直接:当QPS超过阈值就进行限流
  • 关联:当关联的资源达到阈值,就限流自己
  • 链路:只记录指定链路上的流量,即指定资源从入口资源进来的流量如果达到阈值就限流
流控效果
快速失败

​ 直接失败,抛异常
​ 相关源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

Warm Up(预热)

​ 根据codeFactor(冷加载因子,默认值为3),从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
​ 即如果阈值为100,冷加载因子为3,预热时长为10秒,那么就会用100 / 3作为最初的阈值,经过10秒之后才会将阈值达到100,进而进行限流,意思就是让允许通过的流量缓慢增加,在达到一定的时间之后才达到阈值这样会更好的保护微服务

排队等待

​ 匀速排队,让请求以均匀的速度通过,阈值类型必须设置成QPS,否则无效。此种模式可适用于应对突发流量的场景
相关源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。

配置文件修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server.port=8083
# 配置中心url
spring.application.name=order-server
#注册中心地址
spring.cloud.nacos.server-addr=192.168.64.128:8848
#添加sentinel依赖后 暴露/actuator/sentinel端点
# 开启健康检查
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=ALWAYS
#打开/关闭掉对Spring MVC端点的保护
spring.cloud.sentinel.filter.enabled=false
#这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了1个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。
spring.cloud.sentinel.transport.port=8731
#指定sentinel控制台的地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080

配置熔断类

异常处理类

注意 熔断处理的 返回值需要和接口的返回值一致,否则会报错

com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

1
2
3
4
5
6
7
8
9
10
11
public class SentinelExceptionHandler {

final static Logger logger = LoggerFactory.getLogger(SentinelExceptionHandler.class);

public static Order blockExceptionHandle(String name, BlockException exception) {
logger.info("sentinel 进行了熔断处理 class:{},errorMsg:{}", SentinelExceptionHandler.class.getName(), exception.getMessage());
Order order = new Order();
order.setUserid("Sentinel 熔断处理函数");
return order;
}
}
限流资源增加注解

对需要限流的资源添加@SentinelResource注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@RequestMapping("/order")
public interface OrderService {

@GetMapping("/createorder/{userid}")
public Order createOrder(@PathVariable("userid") String userId);
}
public interface OrderServiceImpl {
@SentinelResource(value = "sentinelResourceTest", entryType = EntryType.OUT, blockHandlerClass = SentinelExceptionHandler.class, blockHandler = "blockExceptionHandle")
@Override
public Order createOrder(String userId) {
Order order = new Order();
order.setUserid(userId);
order.setMoney(0.0F);
order.setCreatreDate(new Date());
order.setOrderId((long) serverPort);
System.out.println("创建订单,userId:" + userId);
return order;
}
}

启动客户端

启动客户端后,请求几次客户端的接口,否则控制台并不会显示出客户端。访问控制台主页,可以看到order-server个应用。

该客户端的http server端口号就是配置文件中配置的 spring.cloud.sentinel.transport.port=8731

如果控制台以及客户端都是在本地电脑上启动的,应注意控制台自身也会启动一个http server,并且默认端口号是8719,如果客户端也采用默认的8719,则客户端启动后会出现冲突,客户端会自动修改该端口号,可能改成8720或者其他未占用的端口。

添加限流规则

测试熔断效果

访问 http://localhost:8083/order/createorder/1

快速刷新,可以看到界面中轮流出现如下返回:

同时控制台打印如下:

1
2020-06-10 11:33:55.387  INFO 27720 --- [nio-8083-exec-3] c.s.o.s.SentinelExceptionHandler         : sentinel 进行了熔断处理 class:com.springcloud.order.senttinel.SentinelExceptionHandler,errorMsg:null

配置熔断降级

Sentinel除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。

Sentinel控制台配置降级规则,即断路器模式,Sentinel目前只有断路器三态中的打开和关闭,没有半开状态

衡量标准

Sentinel以三种方式衡量被访问的资源是否处理稳定的状态

平均响应时间

​ 平均响应时间 (DEGRADE_GRADE_RT):当资源的平均响应时间超过阈值(DegradeRule 中的 count,以 ms 为单位)之后,资源进入准降级状态。接下来如果持续进入 5 个请求,它们的 RT 都持续超过这个阈值,那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回(抛出 DegradeException)。在下一个时间窗口到来时, 会接着再放入5个请求, 再重复上面的判断.

异常比例

​ 异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

异常数

​ 异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。

熔断方法

调用接口
1
2
3
4
5
6
7
@RestController
@RequestMapping("/order")
public interface OrderService {

@PostMapping("/query")
public String query(@RequestBody Order order);
}

启动控制台、客户端

请求一次接口http://localhost:8083/order/query,在控制台就可以看到该应用。

异常熔断

接口实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Component
public class OrderServiceImpl implements OrderService {

@SentinelResource(value = "sentinelResourceQuery", fallback = "fallback")
public String query(Order order) {
System.out.println(order);
System.out.println("调用查询接口,线程号:" + Thread.currentThread().getId());
if (Thread.currentThread().getId() % 2 == 0) {
int k = 1 / 0;
System.out.println("出现了异常,线程号:" + Thread.currentThread().getId());

}
return "调用查询接口...";
}


/**
* 降级方法
*
* @return
*/
public String fallback(Order order) {
System.out.println("调用降级方法,线程号:" + Thread.currentThread().getId());
return "调用降级方法...";
}
}

添加降级规则

根据异常数,异常数大于2,则熔断降级(十秒要连续超过2个请求异常才会熔断)

测试

用postman多访问几次就会触发熔断,会发现触发后 一直是熔断状态,只有停止请求,等待10s后才恢复请求

打印日志

1
2
3
4
5
6
7
8
9
10
11
12
调用查询接口,线程号:60
调用降级方法,线程号:60
调用降级方法,线程号:61
调用降级方法,线程号:62
调用降级方法,线程号:63
调用降级方法,线程号:64
调用降级方法,线程号:65
调用降级方法,线程号:66
调用降级方法,线程号:57
调用降级方法,线程号:58
调用降级方法,线程号:59
调用降级方法,线程号:60

超时熔断

修改业务代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SentinelResource(value = "sentinelResourceQuery", fallback = "fallback")
public String query(Order order) {
System.out.println(order);
System.out.println("调用查询接口,线程号:" + Thread.currentThread().getId());
if (Thread.currentThread().getId() % 2 == 0) {
//休眠1s 模拟超时等待
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("出现了超时,线程号:" + Thread.currentThread().getId());

}
return "调用查询接口...";
}
修改降级规则

根据响应时间,大于10毫秒,则熔断降级(要连续超过5个请求超过10毫秒才会熔断)

Jmeter测试

压力测试后降级示意图

热点限流

​ 比如我们想要对一段时间内频繁访问的用户 ID 进行限制,又或者我们想统计一段时间内最常购买的商品 ID 并针对商品 ID 进行限制。那这里的用户 ID 和商品 ID 都是可变的资源,通过原先的固定资源已经无法满足我们的需求了,这时我们就可以通过 Sentinel 为我们提供的 热点参数限流 来达到这样的效果。

什么是热点

​ 首先我们需要知道什么是热点,热点就是访问非常频繁的参数。

​ 例如我们大家都知道的爬虫,就是通过脚本去爬取其他网站的数据,一般防爬虫的常用方法之一就是限制爬虫的 IP,那对应到这里 IP 就是一种热点的参数。

​ 那么 Sentinel 是怎么知道哪些参数是热点,哪些参数不是热点的呢?Sentinel 利用 LRU 策略,结合底层的滑动窗口机制来实现热点参数统计。LRU 策略可以统计单位时间内,最近最常访问的热点参数,而滑动窗口机制可以帮助统计每个参数的 qps。

​ 说简单点就是,Sentinel 会先检查出提交过来的参数,哪些是热点的参数,然后在应用热点参数的限流规则,将qps 超过设定阈值的请求给 block 掉,整个过程如下图所示:

定义热点方法

定义接口
1
2
3
4
5
6
7
@RestController
@RequestMapping("/order")
public interface OrderService {

@RequestMapping("/testHot")
public String testHot(@RequestParam(required = false) String a, @RequestParam(required = false) String b);
}
定义实现类
1
2
3
4
5
@SentinelResource(value = "sentinelResourceTestHot")
@Override
public String testHot(String a, String b) {
return a + "_" + b;
}

我们增加了testHot方法,并在此方法上面增加了注解@SentinelResource;对于 @SentinelResource 注解方式定义的资源,若注解作用的方法上有参数,Sentinel 会将它们作为参数传入 SphU.entry(res, args)。

比如上面的方法里面 a 和 b 会分别作为第一个和第二个参数传入 Sentinel API,从而可以用于热点规则判断。

配置规则

限流模式只有QPS模式(这才叫热点)。

参数索引代表@SentinelResource注解的方法参数,0代表第一个参数,1代表第二个参数。

单机阀值以及统计窗口时长表示在此窗口时间超过阀值就限流。

热点参数

进行访问测试

访问 http://localhost:8083/order/testHot?a=1

第一个参数有值的话,1秒的QPS为1,超过就限流

非热点参数

访问 http://localhost:8083/order/testHot?b=1

不管刷新多少次都不会进行限流

带上了第二个参数b,没有第一个参数a,不触发热点限流

特定参数

​ 上文中我们配置了针对参数索引的热点限流,有时候我们需要对参数的值做一些特殊的规格;如:上文表示了请求地址如果有参数a,就触发热点限流QPS为1。

​ 但有业务需要参数a=5时,QPS要放大(即业务需要a=5时,阀值调大点,如:5);应用的场景某些秒杀商品,分配给他们大点的QPS。那怎么设置?

Sentinel控制台要在热点规则那边,点击编辑按钮

参数例外项:上图配置的含义就是hot资源名配置了第一个参数进行热点限流,阀值为1;第一参数值为5时,限流阀值变为5

注意参数的类型要跟方法中的参数一致

jmeter中测试

我们发现前五个正在请求超过5以后的 都会被限流,这样做到动态根据参数限流

测试验证了参数值为5时,阀值变大了。

观察控制台流量图

适用场景

​ 热点参数其实说白了就是特殊的流控,流控设置是针对整个请求的;但是热点参数他可以设置到具体哪个参数,甚至参数针对的值,这样更灵活的进行流控管理。

​ 一般应用在某些特殊资源的特殊处理,如:某些商品流量大,其他商品流量很正常,就可以利用热点参数限流的方案。

注意点

​ 热点参数的注意点,参数必须是基本类型或者String

小结

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

系统规则

背景

​ 长期以来,系统保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。这个思路给我们带来了不可避免的两个问题:

load 是一个结果

​ load 是一个“结果”,如果根据 load 的情况来调节流量的通过率,那么就始终有延迟性。也就意味着通过率的任何调整,都会过一段时间才能看到效果。当前通过率是使 load 恶化的一个动作,那么也至少要过 1 秒之后才能观测到;同理,如果当前通过率调整是让 load 好转的一个动作,也需要 1 秒之后才能继续调整,这样就浪费了系统的处理能力。所以我们看到的曲线,总是会有抖动。

恢复慢

​ 想象一下这样的一个场景(真实),出现了这样一个问题,下游应用不可靠,导致应用 RT 很高,从而 load 到了一个很高的点。过了一段时间之后下游应用恢复了,应用 RT 也相应减少。这个时候,其实应该大幅度增大流量的通过率;但是由于这个时候 load 仍然很高,通过率的恢复仍然不高

解决办法

​ 我们应该根据系统能够处理的请求,和允许进来的请求,来做平衡,而不是根据一个间接的指标(系统 load)来做限流。最终我们追求的目标是 在系统不被拖垮的情况下,提高系统的吞吐率,而不是 load 一定要到低于某个阈值。如果我们还是按照固有的思维,超过特定的 load 就禁止流量进入,系统 load 恢复就放开流量,这样做的结果是无论我们怎么调参数,调比例,都是按照果来调节因,都无法取得良好的效果。

​ Sentinel 在系统自适应保护的做法是,用 load1 作为启动自适应保护的因子,而允许通过的流量由处理请求的能力,即请求的响应时间以及当前系统正在处理的请求速率来决定。

支持模式

Load 自适应

​ Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。

CPU usage

​ CPU usage(1.5.0+ 版本),当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。

平均 RT

​ 当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

并发线程数

​ 当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护

入口 QPS

​ 当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

配置规则

总结来说就是一个全局的配置,而不是针对单个请求的配置

评论