目录
链接
Sentinel 介绍
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
Sentinel 功能
流量控制
流量控制有以下几个角度:
资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
运行指标,例如 QPS、线程池、系统负载等;
控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
熔断降级
Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。
使用场景:通常在弱依赖程序中使用,如积分功能,可以降级写日志,之后再进行增添
熔断降级设计限制的手段
通过并发线程数进行限制、通过响应时间对资源进行降级
系统负载保护
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
与Hystrix对比
快速开始(使用代码的方式实现)
1.引入 Sentinel 依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.5</version>
</dependency>
2.定义资源
资源 是 Sentinel 中的核心概念之一。最常用的资源是我们代码中的 Java 方法。 当然,您也可以更灵活的定义你的资源,例如,把需要控制流量的代码用 Sentinel API SphU.entry(“HelloWorld”) 和 entry.exit() 包围起来即可。在下面的例子中,我们将 System.out.println(“hello world”); 作为资源(被保护的逻辑),用 API 包装起来。参考代码如下:
public static void main(String[] args) {
// 初始化配置规则.
initFlowRules();
while (true) {
// 1.5.0 版本开始可以直接利用 try-with-resources 特性
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保护的逻辑
System.out.println("hello world");
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("blocked!");
}
}
}
您也可以通过@SentinelResource 注解,来定义我们的资源,类似于下面的代码:
添加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>x.y.z</version>
</dependency>
添加配置
若您的应用使用了 Spring AOP,您需要通过配置的方式将 SentinelResourceAspect 注册为一个 Spring Bean:
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
注解使用
//blockHandler:流控降级后的处理方法(默认该方法声明在同一个类中)
//fallback:接口出现异常,交给fallback指定的方法进行处理
//blockHandler:使用优先级更高
@SentinelResource(value = "HelloWorld", blockHandler = "exceptionHandler", fallback = "helloFallback" )
public user helloWorld() {
// 资源中的逻辑
return new user();
}
// Fallback 流控降级函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public user helloFallback(long s) {
return new user();
}
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public user exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return new user();
}
这样,helloWorld() 方法就成了我们的一个资源。
3.定义流控规则
接下来,通过流控规则来指定允许该资源通过的请求次数,例如下面的代码定义了资源 HelloWorld 每秒最多只能通过 20 个请求。
private static void initFlowRules(){
//创建流控规则
List<FlowRule> rules = new ArrayList<>();
//流控
FlowRule rule = new FlowRule();
//为哪一个资源流控
rule.setResource("HelloWorld");
//设置流控规则 QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//设置受保护的资源阈值 设置一秒QPS 为 20
rule.setCount(20);
rules.add(rule);
//加载配置好的规则
FlowRuleManager.loadRules(rules);
}
4.定义熔断降级规则
熔断降级多用在消费方,流量控制多用于生产方
熔断降级规则(DegradeRule)包含下面几个重要的属性:
private static void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule(KEY)
//设置规则策略
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
// 触发熔断异常数
.setCount(20)
// Circuit breaker opens when slow request ratio > 60%
.setSlowRatioThreshold(0.6)
//触发熔断最小请求数
.setMinRequestAmount(50)
//统计时长(单位毫秒):默认一秒
.setStatIntervalMs(1000);
// 熔断时长(秒):10秒内调用该方法走服务降级
.setTimeWindow(10)
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
Sentinel 控制台
注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本
获取 Sentinel 控制台
本次下载为:sentinel-dashboard-1.8.1.jar
启动
cmd命令启动
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
其中 -Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080。
访问端口默认为8080
java -Dserver.port=8088 -jar sentinel-dashboard-1.8.1.jar
点击浏览器访问Sentinel 控制台:8088
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel
也可以创建一个批处理文件 sentinel-start.bat 执行启动:
java -Dserver.port=8088 -jar sentinel-dashboard-1.8.1.jar
pause
双击执行
客户端接入控制台
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>x.y.z</version>
</dependency>
后期都不使用这种方式,用一个start就行了
Spring Cloud Alibaba整合sentinel
项目配置
依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
配置
spring:
application:
#nacos 服务注册名称, 远程调用服务使用
name: order-openfeign
cloud:
sentinel:
transport:
# 要使用localhost,不然实时监控不显示
dashboard: localhost:8858
Sentinel 控制台流控配置
流量控制配置
1. QPS流控
流量控制的使用场景
新增QPS规则
查看
当对设置规则的接口超量访问时返回:Blocked by Sentinel (flow limiting) - ->> 哨兵阻挡(流量限制)
2.线程数流控
该接口正在执行接口任务的线程数不能超过指定值
3.服务降级配置
测试以上配置
//注入feign远程调用接口
@Autowired
private StockFeignService stockFeignService;
@GetMapping("/add")
@SentinelResource(value = "add", blockHandler = "addBlockHandler", fallback = "addFallback")
public Boolean add(Integer num){
logger.info("进入OrderController.add");
if (num > 1){
Integer xx = Integer.valueOf("xx");
}
return stockFeignService.add(num);
}
public Boolean addBlockHandler(Integer num, BlockException blockException){
logger.info("blockHandler:流控后的处理方法(默认该方法声明在同一个类中) num:{}, exception:{}", num, blockException.getMessage());
return false;
}
public Boolean addFallback(Integer num){
logger.info("fallback:服务出错降级后的处理方法 num:{}", num);
return false;
}
@SentinelResource :指定自定义服务异常的处理方法
结论:
- 当服务未发生异常,QPS超过时,服务执行blockHandler方法返回
- 当服务发生错误时,服务执行fallback,当超过指定异常数时会服务降级,此时访问服务都只会执行blockHandler,直到服务降级时间结束。
- blockHandler:使用优先级更高
4.关联流控
添加关联资源,用来限制本资源流量
场景:下订单QPS变大时,限制订单查询流量
5.链路流控
test1 和 test2方法都要调用getUser方法,
定义限流资源方法
block方法注意
- 此方法不走统 一 BlockException异常
- 方法为public
- 返回值类型与限流资源同类型
- 参数要与限流资源相同且要添加BlockException
控制台配置
访问没有效果
解决:在yml文件中添加
控制台链路显示为
结果:getUser访问QPS超量,此时访问test2显示被流控,访问test1正常
全局统一流控异常(BlockException)处理
全局统一流控异常返回:当异常为公共的返回时,可以自定义统一处理
实现 BlockExceptionHandler:
@Slf4j
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
// e.getRule() 资源 规则的详细信息
log.info("自定义全局 BlockExceptionHandler e:{}", e.getRule());
AjaxResult ajaxResult = null;
if (e instanceof FlowException){
//超过QPS流控进入
ajaxResult = AjaxResult.error(100, "接口限流了");
}else if (e instanceof DegradeException){
//异常数到达降级规则执行
ajaxResult = AjaxResult.error(101, "服务降级了");
}else if (e instanceof ParamFlowException){
ajaxResult = AjaxResult.error(102, "热点参数限流了");
}else if (e instanceof SystemBlockException){
ajaxResult = AjaxResult.error(103, "触发系统保护规则了");
}else if (e instanceof AuthorityException){
ajaxResult = AjaxResult.error(104, "授权规则不通过");
}
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print(ajaxResult.toString());
}
}
新建测试接口,并在控制台设置流控规则
@GetMapping("/my")
public AjaxResult myBlockException(Integer num){
logger.info("测试全局异常");
if (num > 1){
Integer xx = Integer.valueOf("xx");
}
return success(true);
}
Sentinel 控制台流控效果
Sentinel 控制台流控效果选项如下图所示:
快速失败
快速失败是默认的处理方式,当请求达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。
例如我们设置阈值为一秒一个请求,当我们请求QPS超过阈值时候,在浏览器中会直接报错:
Warm Up(预热/冷启动)
阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。
warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 maxThreshold / coldFactor,持续指定时长后,逐渐提高到maxThreshold值。而coldFactor的默认值是3.
场景:秒杀机器最大QPS为10,设置阈值maxThreshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 = 3,然后在5秒内逐渐增长到10,防止激增流量。
案例
需求:给/order/{orderId}这个资源设置限流,最大QPS为10,利用warm up效果,预热时长为5秒。
1)配置流控规则:
2)Jmeter访问测试
20秒总共访问200次,QPS为10。
结果:
结论:
- 刚刚启动时,大部分请求失败,成功的只有3个,说明QPS被限定在3
- 随着时间推移,成功比例越来越高,Wamp up效果的阈值是逐渐递增的。
排队等待(脉冲流量)
排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
- 当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。
- 目的是让服务以平滑的QPS曲线来消费,不是第一秒100QPS,第二秒1QPS。
案例
需求:给/order/{orderId}这个资源设置限流,最大QPS为10,利用排队的流控效果,超时时长设置为5s
1)添加流控规则
2)Jmeter测试
QPS为15,已经超过了我们设定的10。
如果是之前的 快速失败、warmup模式,超出的请求应该会直接报错。
但是我们看看队列模式的运行结果:
全部都通过了。
再去sentinel查看实时监控的QPS曲线:
- QPS非常平滑,一致保持在10,超出的请求会被放入队列,因此请求响应时间会越来越长。
- 当队列满了以后或超时,才会有部分请求失败。
熔断降级
在分布式架构复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个服务链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。
熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
熔断策略
Sentinel 提供以下几种熔断策略:
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
- 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效
发送熔断后的处理逻辑示例
- 提供fallback实现服务降级
- 返回错误result
- 读缓存(DB访问降级)
openfeign整合sentinel
配置:添加openfeign对 sentinel的支持
feign:
sentinel:
enabled: true
配置feign远程调用服务接口
/**
* 2 配置feign远程调用接口
* @ FeignClient(name = "Macos服务注册名", path = "/服务提供者控制层入口参数")
* fallback:服务降级的处理
*/
@FeignClient(name = "stock-service", path = "/stock" , fallback = StockFeignFallbackService.class)
public interface StockFeignService {
/**
* 把服务提供者控制层接口直接复制过来, 格式一样
*/
@GetMapping("/add")
boolean add(@RequestParam("num") Integer num);
@GetMapping("/add2")
String add2(@RequestParam("num") Integer num);
}
服务降级处理
/**
* openfeign整合sentinel:服务降级处理
* 流控降级会进入这里
*/
@Component
public class StockFeignFallbackService implements StockFeignService {
@Override
public boolean add(Integer num) {
System.out.println("StockFeignFallbackService.add openfeign整合sentinel实现服务降级处理 ");
return false;
}
@Override
public String add2(Integer num) {
System.out.println("StockFeignFallbackService.add2 服务降级处理啦!");
return "你被服务降级处理啦!";
}
}
使用
//注入feign远程调用接口
@Resource
private StockFeignService stockFeignService;
@GetMapping("/add2")
public AjaxResult add2(Integer num){
logger.info("测试统一流控和feign整合sentinel服务降级处理");
return success(stockFeignService.add2(num));
}
然后就是配置 sentinel控制台,流控及服务降级都会进入StockFeignFallbackService 处理返回
热点参数流控
实现
控制台配置
新增后修改 - 高级选项(添加接口参数的热点值)
运行测试。