Sentinel 初级使用
根据 alibaba 官网说明,Sentinel 在 1.8.0 版本有较大的改进,以下使用的 Sentinel 版本为 1.7.0
sentinel 资源链接 提取码:kui6
1. Sentinel 的组成
-
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
-
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
1.控制台启动
-
https://github.com/alibaba/Sentinel/releases 下载 sentinel-dashboard-1.7.0.jar 到本地
-
运行:java -jar sentinel-dashboard-1.7.0.jar
-
初始账号密码:sentinel
-
启动 nacos (负责 Sentinel 规则持久化)
2. 创建新项目测试
-
创建 Module:cloudalibaba-sentinel-service8401
-
POM
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 负责 sentinel 持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- YML
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 # 控制台地址
port: 8719 #默认8719,应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用HttpServer
management:
endpoints:
web:
exposure:
include: '*'
注:这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server ,该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。
- 主启动
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401{
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
- 业务类
@RestController
@Slf4j
public class FlowLimitController{
@GetMapping("/testA")
public String testA() {
return "------testA";
}
@GetMapping("/testB")
public String testB() {
return "------testB";
}
}
- 启动
-
启动 sentinel 8080:java -jar sentinel-dashboard-1.7.0.jar
-
启动微服务 8401
注:Sentinel 采用的懒加载模式,我们需要访问一次,sentinel 才会加载监控的业务
2. 流控规则
博客:
sentinel流控模式:链路模式失效问题
Spring Cloud Alibaba:Sentinel 流控规则
1. 直接:达到条件直接失败
2. 关联:关联的微服务达到条件时,指定资源失败;
3. 链路
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细。
下面建议将 spring-cloud-alibaba 的版本改为 2.1.1.RELEASE
- 编写一个 service
@Service
public class FlowControlServiceImpl implements FlowControlService {
@Override
@SentinelResource(value = "testC") // Sentinel 默认会将 controller 映射为资源名,我们也可以用此注解表明资源名
public void testC() {
System.out.println("------testC");
}
public void testC_Handler(){
System.out.println("------testC_Handler");
}
}
- 在 controller 的两个接口中,调用这个方法
@RestController
@Slf4j
public class FlowLimitController{
@Resource
private FlowControlService flowControlService;
@GetMapping("/testA")
public String testA() {
flowControlService.testC();
return "------testA";
}
@GetMapping("/testB")
public String testB() {
flowControlService.testC();
return "------testB";
}
}
- 配置文件中关闭sentinel官方的CommonFilter实例化
spring:
cloud:
sentinel:
filter:
enabled: false
- 添加配置类,自己构建 CommonFilter 实例
@Configuration
public class FilterContextConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CommonFilter());
registrationBean.addUrlPatterns("/*");
// 入口资源关闭聚合
registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registrationBean.setName("sentinelFilter");
registrationBean.setOrder(1);
return registrationBean;
}
}
- 在添加完链路流控规则后,测试,发现资源 testC 被限制了
3.降级规则
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
1.熔断策略
-
平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下来的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动熔断(抛出 DegradeException)。注意:窗口期过后关闭断路器RT 最大4900 ms,更大的需要通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。
-
异常比例(DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(Degrade 中的 count)之后,资源进入降级状态,即在接下来的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动降级。异常比例的范围是 [0.0, 1.0],代表 0% - 100%
-
异常数(DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断之后仍可能再进入熔断
说明:
-
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如:调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
-
Sentinel的断路器是没有半开状态的(1.8.0 之后有较大改动,新增半开状态)。半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix
降级实测
- 新增 controller 接口
@GetMapping("/testD")
// 资源最好不要和 mapping 一样
@SentinelResource(value = "testD", fallback = "fallback")
public String testD(){
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
log.info("testD 测试RT");
return "------testD";
}
public String fallback(){
System.out.println("fallback");
return "fallback";
}
- 新增降级规则
- 测试效果
- 其他配置示例
4. 热点 Key 限流
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的TopK数据,并对其访问进行限制。比如︰
- 商品ID为参数,统计一段时间内最常购买的商品ID并进行限制
- 用户ID为参数,针对一段时间内频繁访问的用户ID进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel利用LRU策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
实测
- 新增 controller 接口
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",fallback = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2) {
int age = 10/0;
return "------testHotKey";
}
//兜底方法
public String deal_testHotKey (String p1, String p2){
return "------deal_testHotKey,o(╥﹏╥)o";
}
@SentinelResource 的使用
-
value
:资源名称,必需项(不能为空); -
entryType
:entry 类型,可选项(默认为EntryType.OUT
) -
blockHandler
/blockHandlerClass
:blockHandler
对应处理BlockException
的函数名称,可选项。blockHandler
函数签名和位置要求:-
函数访问范围需要是 public;
-
返回类型需要与原方法相匹配;
-
参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为
BlockException
; -
blockHandler
函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass
为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
-
-
fallback
/fallbackClass
:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:-
返回值类型必须与原函数返回值类型一致;
-
方法参数列表需要和原函数一致,或者可以额外多一个
Throwable
类型的参数用于接收对应的异常。 -
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定
fallbackClass
为对应的类的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 支持在类级别进行配置。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。