Sentinel特性

Sentinel分两个部分

  • 核心库(Java客户端)不依赖任何框架/库,能够运行于所有Java运行时环境,同时对Dubbo/SpringCloud等框架也有较好的支持
  • 控制台(Dashboard)基于SpringBoot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器

Sentinel控制台安装步骤

  1. 下载地址

  2. 运行命令(前提Java8环境;8080端口)

    1
    java -jar sentinel-dashboard-1.8.6.jar
  3. 访问sentinel管理界面(账号密码均为sentinel)

初始化监控

启动Nacos http://localhost:8848/nacos

1.新建cloudalibaba-sentinel-service-8401

2.引入pom

3.配置yml

4.添加主启动

5.处理业务类

6.测试

启动8401,由于Sentinel采用懒加载

执行一次访问http://localhost:8401/testA和http://localhost:8401/testB

流控规则

  • 资源名:唯一名称,默认请求路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单机阈值:
    • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值时,进行限流
    • 线程数:当调用该api的线程数达到阈值时,进行限流
  • 是否集群,不需要集群
  • 流控模式:
    • 直接:api到达限流条件时,直接限流
    • 关联:当关联的资源达到阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来时的流量,如果达到阈值,就进行限流)【api级别的针对来源】
  • 流控效果:
    • 快速失败::直接失败,抛异常
    • Warm Up:根据coldFactor(冷加载因子,默认3)的值,从阈值/coldFactor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

降级规则

基本介绍

RT(平均响应时间,秒级)

平均响应时间 超出阈值且在时间窗内通过的请求>=5,两个条件同时满足后触发降级,窗口期过后关闭断路器,RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

异常比例(秒级)

QPS>=5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

Sentinel熔断器降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)

Sentinel断路器是没有半开状态

半开的状态系统自动检测是否有请求异常,没有异常就去关闭断路器恢复使用,有异常则继续打开断路器不可用

RT

异常比例 (ERROR_RATIO)

当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

异常数 (ERROR_COUNT)

当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

热点规则

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

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

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "------testA";
}

@GetMapping("/testB")
public String testB() {
log.info(Thread.currentThread().getName()+"\t"+"...testB");
return "------testB";
}

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "------testHotKey";
}

public String deal_testHotKey(String p1, String p2, BlockException e) {
return "-------deal_testhotkey";
}
}

配置

@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")

方法testHotKey里面的第一个参数只要QPS超过每秒一次,马上降级处理

参数例外项

特例情况

  • 普通: 超过1s后阈值马上被限流
  • 特殊:期望p1参数为某特殊值时,它的限流值和平时不一样

配置


@SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理,主管配置出错,运行时异常不管的

系统规则

参考文档

Sentinel 系统自适应过载保护从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

SentinelResource配置

参考文档

按资源名称限流+后续处理

1.启动Nacos

2.启动Sentinel

3.修改cloudalibaba-sentinel-service-8401

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
public class RateLimitController {

@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200, "按资源名限流测试成功", new Payment(2023L, "serial001"));
}

public CommonResult handleException(BlockException ex) {
return new CommonResult(444, ex.getClass().getCanonicalName() + "\t服务不可用");
}
}

4.配置流控规则

测试流控后,将8401关闭,发现流控规则消失,无法持久化

按URL地址限流+后续处理

1
2
3
4
5
@GetMapping("/byUrl")
@SentinelResource(value = "byUrl", blockHandler = "handleException")
public CommonResult byUrl() {
return new CommonResult(200, "按url限流测试成功", new Payment(2023L, "serial001"));
}

上述两种兜底方案存在问题

  1. 系统默认的,没有体现自己的业务要求
  2. 依照现有条件,自定义的处理方法和业务代码耦合在一起,不直观
  3. 每个业务方法都添加一个兜底的,造成代码膨胀
  4. 全局统一的异常处理方法没有体现

客户自定义限流处理逻辑

1.创建CustomerBlockHandler类用于自定义限流处理逻辑

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

public static CommonResult handlerException1(BlockException ex) {
return new CommonResult(444, "按客户自定义,全局异常处理器----1");
}

public static CommonResult handlerException2(BlockException ex) {
return new CommonResult(444, "按客户自定义,全局异常处理器----2");
}
}

2.controller

1
2
3
4
5
@GetMapping("/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException1")
public CommonResult customerBlockHandler() {
return new CommonResult(200, "按客户自定义", new Payment(2023L, "serial003"));
}

@SentinelResource 注解

注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT
  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • fallback/fallbackClassblockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:

    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

1.8.0 版本开始,defaultFallback 支持在类级别进行配置。

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

sentinel主要有三个核心API

  • SphU定义资源
  • Tracer定义统计
  • ContextUtil定义上下文

服务熔断

Sentinel整合Ribbon+OpenFeign+fallback

Ribbon系列

提供者9003/9004

1.新建cloudalibaba-provider-payment-9003/9004

2.引入pom

3.配置yml

4.添加主启动

5.处理业务

消费者84

1.新建cloudalibaba-consumer-nacos-order-84

2.引入pom

3.配置yml

4.添加主启动

5.处理业务

目的

fallback负责运行时异常

blockHandler负责配置违规

若blockHandler和fallback都进配置,则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑

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
28
29
30
31
32
33
34
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";

@Resource
private RestTemplate restTemplate;

@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //没有配置
//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
//@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
@SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}

return result;
}
//本例是fallback
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
//本例是blockHandler
public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}

}

Feign系列

**Feign组件一般是消费侧**
1
2
3
4
5
6
7
8
9
10
11
12
13
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

@Component
public class PaymentFallbackService implements PaymentService {
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
1
2
3
4
5
6
7
@Resource
private PaymentService paymentService;

@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
return paymentService.paymentSQL(id);
}

测试84调用9003,此时9003微服务宕机,消费者84会被降级

熔断框架比较

Sentinel Hystrix resilience4j
隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号量隔离 信号量隔离
熔断降级策略 基于响应时间、异常比率、异常数 基于异常比率 基于异常比率、响应时间
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava) Ring Bit Buffer
动态规则配置 支持多种数据源 支持多种数据源 有限支持
扩展性 多个扩展点 插件形式 接口形式
基于注解的支持 支持 支持 支持
限流 基于QPS、支持基于调用关系的限流 有限的支持 Rate Limiter

规则持久化

一旦重启应用,Sentinel规则将消失,生产环境需要将配置进行持久化

将限流规则持久化进Nacos进行保存,只要刷新8401某个rest地址,Sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上的Sentinel上的流控规则持续有效

步骤

修改cloudalibaba-sentinel-service-8401

pom

1
2
3
4
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

yml(添加Nacos数据源配置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow

添加Nacos业务规则配置

1
2
3
4
5
6
7
8
9
{
"resource":"/rateLimit/byUrl",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controllerBehavior":0,
"clusterMode":false
}
属性 说明
resource 资源名称
limitApp 来源应用
grade 阈值类型,0表示线程数,1表示QPS
count 单机阈值
strategy 流控模式,0表示直接,1表示关联,2表示链路
controllerBehavior 流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
clusterMode 是否集群

启动8401,刷新Sentinel