Sentinel服务限流、降级、热点规则
Sentinel服务限流、降级、热点规则
git地址: https://github.com/alibaba/Sentinel
中文文档: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
sentinel实际和hystrix的作用一样,实现服务降级、熔断等。但是hystrix的不足之处大概有:1.需要程序员手工搭建监控平台;2.没有一套web界面可以给我们进行细粒度化的配置。Sentinel也是实现流量控制、速率控制、服务熔断、服务降级。Sentinel有的优点如下:1.单独的组件,可以独立出来。2.直接界面化的细粒度统一配置。
1.下载安装
1.下载jar包
到git https://github.com/alibaba/Sentinel/releases 下载即可。例如我下载的是:
sentinel-dashboard-1.8.0.jar
2. 启动
直接以jar包的方式运行即可。
java -jar ./sentinel-dashboard-1.8.0.jar
3.访问即可
默认的端口是8080,启动后访问,登录: sentinel/sentinel
2. 新建项目使用sentinel初始化监控
新建一个项目,注册中心为nacos,端口8848; 熔断流量监控使用sentinel,端口8080.
1.新建项目cloudalibaba-sentinel-service8401
2.修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud</artifactId>
<groupId>cn.qz.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-sentinel-service8401</artifactId>
<dependencies>
<!--引入自己抽取的工具包-->
<dependency>
<groupId>cn.qz.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--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>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringBoot整合Web组件+actuator -->
<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>
<!--日常通用jar包配置-->
<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>
</project>
3.yml配置
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
# 默认就是8719端口,如果占用会依次+1开始扫描
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
4.主启动类
package cn.qz.cloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Author: qlq
* @Description
* @Date: 22:06 2020/11/25
*/
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
5.测试Controller
package cn.qz.cloud.alibaba.controller;
import cn.qz.cloud.utils.JSONResultUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: qlq
* @Description
* @Date: 22:07 2020/11/25
*/
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public JSONResultUtil<String> testA() {
return JSONResultUtil.successWithData("testA");
}
@GetMapping("/testB")
public JSONResultUtil<String> testB() {
return JSONResultUtil.successWithData("testB");
}
}
6.启动后到nacos查看服务列表
7. 访问sentinel
需要注意sentinel采用的是懒加载,如果直接启动服务没有访问,sentinel是不会拦截到请求的。所以需要访问几次服务然后到sentinel中查看。sentinel控制台如下:
3. sentinel流控
1.实时监控
可以实时的查看每个请求的访问QPS情况。
2.簇点链路
和上面1差不多。展示最近的访问情况。
3.流控规则
新增流控规则的时候,界面如下:
概念性问题:
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阈值
(1)QPS(每秒钟的请求数):当掉该api达到阈值的时候,进行限流
(2)线程数: 当调用该API的线程数达到阈值时,进行限流
- 是否集群:不需要集群
- 流控模式
(1)直接:直接限流
(2)关联:当关联的资源达到阈值时,限流自己
(3)链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
- 流控效果
(1)快速失败:直接失败,抛异常
(2)Warm up:根据code Factor(冷加载印子,默认3)的值,从阈值/cedeFactor,经过预热时长,才达到预设的QPS
(3)排队等待: 匀速排队,让请求匀速的通过,阈值类型必须设置QPS。否则无效。
4. 流控模式使用
- QPS 快速失败
多次访问查看结果如下:
- 线程数直接失败
这个是接收请求,但是当处理的线程数超过1的时候报上面的【Blocked by Sentinel (flow limiting)】。和上面不同的是这个允许请求进去,上面是达到阈值就不让请求进去。
3.关联流控
关联流控就是关联的资源达到阈值,限流自己。比如如下:当B超过QPS为1之后,A限流:
测试方法:
(1) 使用postman或者jmeter并发访问testB,如下:加到collections中,然后没300ms访问一次,访问30次
(2)访问A发现A阻塞
- 链路流控规则
(1)查看簇点链路,/testA的资源入口是sentinel_web_servlet_context
(2) 对A进行链路流控
(3)频繁访问会报Blocked by Sentinel (flow limiting)
5. 流控效果
1.直接快速失败
默认的就是快速失败,抛出阻塞信息。实现类是 com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
2.warm up预热: com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController.WarmUpController
根据code Factor(冷加载印子,默认3)的值,从阈值/cedeFactor,经过预热时长,才达到预设的QPS。比如下面规则表示:初始的时候阈值是10/3 = 3 ;5s的预热时间达到10。
这种模式的应用场景是秒杀系统,开启瞬间会有很多流量上来,很有可能把系统打死,预热就是为了保护系统,让流量缓慢的进来。
- 排队等待
匀速排队,让请求匀速的通过。这种很好理解,就是每秒钟放行多少个请求。如下:每秒钟处理1个,超过的话进行排队;排队时长超过2s报阻塞错误。源码是com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。采用的算法是令牌桶算法。
4.sentinel熔断降级
参考: https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
提供以下几种熔断策略:
(1)慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
(2)异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
(3)异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
1. 慢调用比例模式
满足两个条件会触发熔断:
1》单位统计时长请求数大于设置的最小值。下面是每秒钟5个。
2》慢请求达到设置的比例。
(1)新增方法
@GetMapping("/testD")
public JSONResultUtil<String> testD() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
return JSONResultUtil.successWithData("testD");
}
(2)jmeter 以每秒钟10次请求访问
(3)curl访问
$ curl http://localhost:8401/testD
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 35 0 35 0 0 249 0 --:--:-- --:--:-- --:--:-- 280Blocked by Sentinel (flow limiting)
(4)上面jmeter每秒钟10次请求大于最小值5;每个请求处理时长超过200ms,满足第二个条件,因此触发降级。
2. 异常比例模式
如下:这个也是需要两个条件。
1》每秒钟请求超过五个
2》异常比例超过50%触发熔断
(1)新增方法
@GetMapping("/testE")
public JSONResultUtil<String> testE() {
int i = 1 / 0;
return JSONResultUtil.successWithData("testE");
}
(2)jmeter每秒钟十个访问
(3)curl访问:发现触发降级
$ curl http://localhost:8401/testE
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 35 0 35 0 0 140 0 --:--:-- --:--:-- --:--:-- 149Blocked by Sentinel (flow limiting)
(4)停掉jmeter再次curl,直接爆程序错误。也就是没触发sentinel熔断
$ curl http://localhost:8401/testE
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0{"timestamp":"2020-12-01T14:41:15.196+0000","status":500,"error":"Internal Server Error","message":"/ by zero","trace":"java.lang.ArithmeticException: / by zero\r\n\tat cn.qz.cloud.alibaba.controller.FlowLimitController.testE(FlowLimitController.java:43)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\r\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.lang.reflect.Method.invoke(Method.java:498)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)\r\n\tat org.springframework.web.servl
3.异常数模式
和上面的区别是根据异常数判断是否需要进行熔断。
5. 热点规则
参考:https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
这个和hystrix挺像的。@SentinelResource代替@HystrixCommand。
1.新增方法
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
public JSONResultUtil<String> testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return JSONResultUtil.successWithData("testHotKey");
}
public JSONResultUtil<String> deal_testHotKey(String p1, String p2, BlockException exception) {
return JSONResultUtil.successWithData("deal_testHotKey"); //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
}
解释:
@SentinelResource注解的value是便于唯一标识,在后面sentinel限流配置的时候可以使用 /testHotKey ,也可以使用 testHotKey唯一标识
blockHandler 类似于hystrixCommand注解的兜底方法。
2.sentinel增加如下配置:
上面的配置规则是:
(1)如果 testHotKey 方法不带p1 参数不进行限流
(2)如果待了p1参数且值不为”1”,阈值为2;如果值为”1”, 阈值可达到20。 (这里需要注意例外项的参数类型只支持8种基本数据类型和String类型)
(3)p2参数不参数限流
6. 系统规则
参考:https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
- 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 达到阈值即触发系统保护。
可以理解为系统规则是全局的限流配置,可以针对服务的全局QPS、机器的CPU等参数进行限流。
7.@SentinelResource
这个注解类似于HystrixCommand注解,可以用于方法的降级处理。
1.常规用法
package cn.qz.cloud.alibaba.controller;
import cn.qz.cloud.utils.JSONResultUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public JSONResultUtil<String> byResource() {
return JSONResultUtil.successWithData("byResource");
}
public JSONResultUtil<String> handleException(BlockException exception) {
return JSONResultUtil.successWithData("handleException");
}
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public JSONResultUtil<String> byUrl() {
return JSONResultUtil.successWithData("byUrl");
}
}
(1)新建两条限流规则:
第一条: 资源名是@SentinelResource 的value属性
第二条: 资源名是URL,等价于上面的@SentinelResource的value属性
(2)测试:
1》 byResource 超出会走自己指定的方法
2》byUrl 超出后走默认的限流方法
(3)存在的问题:
1》系统默认限流,满足不了业务要求。
2》自定义的处理方法和业务代码耦合在一起,不直观。
3》每个方法都添加一个兜底的,代码膨胀加剧。
4》全局统一的处理方法没有体现。
2. 自定义异常处理器
(1)新建异常处理器
package cn.qz.cloud.alibaba.myhandler;
import cn.qz.cloud.utils.JSONResultUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
public class CustomerBlockHandler {
public static JSONResultUtil<Object> handlerException(BlockException exception) {
return JSONResultUtil.errorWithMsg("-1", "error");
}
public static JSONResultUtil<Object> handlerException2(BlockException exception) {
return JSONResultUtil.errorWithMsg("-1", "error2");
}
}
(2)Controller使用@SentinelResource注解指明异常处理器以及方法
@GetMapping("/rateLimit/cusError")
@SentinelResource(value = "cusError", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
public JSONResultUtil<String> cusError() {
return JSONResultUtil.successWithData("cusError");
}
(3)新建流控
(3) 测试超出限制
8.@SentnelResource 补充
主要属性 fallback和blockHandler。fallback管运行异常,blockHandler管配置违规,包括流量超限等。IllegalArgumentException 可以指定忽略的异常。例如:
package cn.qz.cloud.alibaba.controller;
import cn.qz.cloud.bean.Payment;
import cn.qz.cloud.utils.JSONResultUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
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",
exceptionsToIgnore = {IllegalArgumentException.class})
public JSONResultUtil<Payment> fallback(@PathVariable Long id) {
JSONResultUtil<Payment> result = restTemplate.getForObject(SERVICE_URL + "/list/" + id, JSONResultUtil.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//本例是fallback
public JSONResultUtil<Payment> handlerFallback(@PathVariable Long id, Throwable e) {
JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null);
objectJSONResultUtil.setMsg("handlerFallback: " + e);
return objectJSONResultUtil;
}
//本例是blockHandler
public JSONResultUtil<Payment> blockHandler(@PathVariable Long id, BlockException blockException) {
JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null);
objectJSONResultUtil.setMsg("blockHandler : " + blockException);
return objectJSONResultUtil;
}
}
nacos-payment-provider服务只有1、2、3 ID返回数据。测试:
(1)ID为4抛出非法参数异常,这种异常exceptionsToIgnore 忽略兜底
(2)ID为4以后的走fallback
9.sentinel持久化
持久化需要借助nacos,就相当于配置信息存到nacos,然后后nacos读取配置。
1.应用的yml增加如下配置:
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: nacos-order-consumer
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
2.到nacos新建配置
这里需要注意,data Id 和 上面应用中配置的一样。
内容如下:
[
{
"resource": "/consumer/fallback/2",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
解释:
resource:资源名称
limitApp:来源应用
grade:阈值类型,0表示线程数,1表示QPS
count:单击阈值
strategy:流控模式,0表示直接,1表示关联,2表示链路
controlBehavior:流控效果,0表示快速失败,1表示Warm up,2表示排队等待
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
还没有评论,来说两句吧...