SpringCloud Alibaba Sentinel实现熔断与限流
官网:https://github.com/alibaba/sentinel
中文网:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
Docker 安装 Sentinel
#拉取sentinel镜像
docker pull bladex/sentinel-dashboard
#运行sentinel(docker里的sentinel是8858端口)
docker run --name sentinel -d -p 8858:8858 bladex/sentinel-dashboard
#把nacos和mysql也启动起来
访问: http://120.92.164.250:8858/#/login
账号和密码都是sentinel
演示
- 新建模块 cloudalibaba-sentinel-service8401
- pom
<dependencies>
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<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>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-sentinal-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址(改成自己的服务器ip地址,本地用localhost)
server-addr: 120.92.164.250:8848
sentinel:
transport:
#配置Sentin dashboard地址(改成自己的服务器ip地址,本地用localhost)
dashboard: 120.92.164.250:8858
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
- 启动类
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
- 测试,启动8401,然后刷新sentinel后台页面(因为sentinel采用懒加载策略,所以需要调用服务后才在后台显示)
在浏览器分别输入,然后刷新sentinel后台页面:
规则的种类
Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。
Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则。
流量控制规则 (FlowRule)
同一个资源可以同时有多个限流规则,检查规则时会依次检查。
每秒请求数超过1个就会限流。
阀值类型
QPS与线程数的区别
: QPS(每秒请求的数量):当调用该api的QPS达到阀值的时候,进行限流
: 线程数:当调用该API的线程数达到阀值的时候,进行限流
QPS是直接挡在外面,而线程数是有多少个线程在处理,放进来后,有线程是空闲状态就对请求进行处理,都没空闲,就限流
QPS流量控制
直接 : 快速失败
关联:当关联的资源达到阀值时,就限流自己,当与A关联的资源B达到阀值后,就限流A自己
链路:Sentinel 允许只根据某个入口的统计信息对资源限流。
- 当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝、Warm Up、匀速排队。对应 FlowRule 中的 controlBehavior 字段。
- 注意:若使用除了直接拒绝之外的流量控制效果,则调用关系限流策略(strategy)会被忽略。
直接拒绝
- 直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm Up
- Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
匀速排队
- 匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法
降级规则
- RT : (平均响应时间,秒级) 平均响应时间 超出阀值 且 在时间窗口内通过的请求 >=5 ,两哥条件同时满足后触发降级窗口期后关闭断路器
- 异常比例 : (秒级) QPS >=5 且异常比例(秒级统计)超过阀值时,触发降级;时间窗口结束后,关闭降级
- 异常数 :(分钟统计) 超过阀值时,触发降级;时间窗口结束后,关闭降级
系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的load 、cpu使用率、平均RT、入口QPS和并发线程数几个纬度监控应用指标,让系统尽可能的跑在最大吞吐量的同时保证系统整体的稳定性
系统保护规则则是应用整体纬度的,而不是资源纬度的,并且仅对入口流量生效。入口流量指的是进入应用的流量,比如Web 服务或Dubbo 服务端接收的请求,都属于入口流量。
系统则支持以下的模式
- Load 自适应 : 系统的load1作为启发指标,精细自适应系统保护。当系统load1超过启发值,且系统当前的并发数超过估算的系统容量时才会触发系统保护
- CPU usage :当系统CPU使用率超过阀值即触发系统保护
- 平均RT:当单台机器上所有入口流量的平均RT达到阀值时触发,单位是毫秒
- 并发线程数 : 当单台机器上所有入口流量的并发线程数达到阀值时触发系统保护
- 入口 QPS : 当单台机器上所有入口流量的QPS 达到阀值即触发系统保护
针对系统:
热点key限流
何为热点:热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频率最高的Top Key 数据,并且对其访问进行限制:
比如:
- 商品ID作为参数,针对一段时间内最常购买的商品ID进行限制
- 商品ID作为参数,针对一段时间内频繁访问的用户ID进行限制
方法:
@GetMapping("/testB")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testB() {
return "----testB";
}
//兜底方法
public String deal_testHotKey(String p1, String p2, BlockException exception) {
// sentinel的默认提示都是: Blocked by Sentinel (flow limiting)
return "----deal_testHotKey, o(╥﹏╥)o";
}
设置限流 1秒
服务熔断降级
- 除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
@SentineResource
- 按资源名称限流
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200,"按照资源名称限流测试",new Payment(2020L,"serial001"));
}
//兜底方法
public CommonResult handleException(BlockException exception) {
return new CommonResult(444,exception.getClass().getCanonicalName() + "\t 服务不可用");
}
}
- 按URL地址限流
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl",blockHandler = "handleException")
public CommonResult byUrl() {
return new CommonResult(200,"按照byUrl限流测试",new Payment(2020L,"serial002"));
}
- 自定义限流
添加自定义类
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception) {
return new CommonResult(444,"按照客户自定义限流测试,Glogal handlerException ---- 1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(444,"按照客户自定义限流测试,Glogal handlerException ---- 2");
}
}
新增接口
//CustomerBlockHandler
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
public CommonResult customerBlockHandler() {
return new CommonResult(200,"按照客户自定义限流测试",new Payment(2020L,"serial003"));
}
- 更多注解的方式
规则持久化
yml 添加对 datasource
datasource:
ds1:
nacos:
server-addr: 10.211.55.26:8848 #nacos
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
feign:
sentinel:
enabled: true #激活Sentinel 对Feign的支持
实现sentinel配置的持久化。