前言:为什么需要流控降级?
大促时瞬间洪峰流量导致系统超出最大负载,load 飙高,系统崩溃导致用户无法下单
“黑马”热点商品击穿缓存,DB 被打垮,挤占正常流量
调用端被不稳定服务拖垮,线程池被占满,导致整个调用链路卡死
为什么需要流量控制?
流量是非常随机性的、不可预测的。前一秒可能还风平浪静,后一秒可能就出现流量洪峰了(例如双十一零点的场景)。然而我们系统的容量总是有限的,如果突然而来的流量超过了系统的承受能力,就可能会导致请求处理不过来,堆积的请求处理缓慢,CPU/Load 飙高,最后导致系统崩溃。因此,我们需要针对这种突发的流量来进行限制,在尽可能处理 请求的同时来保障服务不被打垮,这就是流量控制。
一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格, 可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务 出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定, 就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务进行 熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。
上面大致介绍了一下服务熔断和限流,现在来看Sentinel
Sentinel: 高可用护航的利器
高度可扩展能力:基础核心 + SPI 接口扩展能力,用户可以方便地扩展流控、通信、 监控等功能。
多样化的流量控制策略(资源粒度、调用关系、流控指标、流控效果等多个维度),提 供分布式集群流控的能力。
热点流量探测和防护。
对不稳定服务进行熔断降级和隔离。
全局维度的系统负载自适应保护,根据系统水位实时调节流量。
覆盖 API Gateway 场景,为 Spring Cloud Gateway、Zuul 提供网关流量控制的能力。
实时监控和规则动态配置管理能力。
Sentinel 主要特性
Sentinel 生态圈
关于Sentinel与Hystrix的区别见:https://yq.aliyun.com/articles/633786/
前期我们也写了一篇关于Hystrix的文章,可以对比着学习SpringCloud-Hystrix解决雪崩
Sentinel 入门案例
1.官网下载Sentinel的jar包,https://github.com/alibaba/Sentinel/releases
启动,用户和密码为sentinel
接上篇dubbo入门案例项目听说Dubbo很不错!
加入sentinel的依赖
<!-- Sentinel依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>复制
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080 #sentinel控制台的请求地址复制
@SentinelResource 注解
注意:注解方式埋点不支持 private 方法。
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
value:资源名称,必需项(不能为空)
entryType:entry 类型,可选项(默认为 EntryType.OUT)
blockHandler / blockHandlerClass:
blockHandler 对应处理 BlockException的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必须为 static 函数,否则无法解析。
fallback /fallbackClass
:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。
defaultFallback
(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。
fallback 函数签名和位置要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass为对应的类的 Class 对象,注意对应的函数必须为 static 函数,否则无法解析。
defaultFallback 函数签名要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必须为 static 函数,否则无法解析。
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控规则
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降级规则
SystemRuleManager.loadRules(List<SystemRule> rules); // 修改系统规则
AuthorityRuleManager.loadRules(List<AuthorityRule> rules); // 修改授权规则复制
@PostConstruct
public void initSentinelRule() {
//熔断规则:5s内调用接口出现异常次数超过5的时候, 进行熔断
List<DegradeRule> degradeRules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource("getUserList");
rule.setCount(5);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);//熔断规则
rule.setTimeWindow(5);
degradeRules.add(rule);
DegradeRuleManager.loadRules(degradeRules);
//流控规则:QPS为2
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource("getUserList");
// QPS控制在2以内
rule1.setCount(2);
// QPS限流
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}复制
新增一条流控
当qps达到2时,会出现sentinel流控效果
@GetMapping("/users")
@SentinelResource(value = "provider",blockHandlerClass = MyHandler.class,blockHandler = "myBlockHandler",
fallbackClass = MyHandler.class,fallback = "myFallBack")
public String getUserList(){
Random random = new Random();
//生成0,5之间的整数
int i = random.nextInt(5);
System.out.println(i);
if(i>2){
throw new RuntimeException("搞事情!!!");
}
List<User> userList = conUserService.getUserList();
System.out.println(userList);
return userList.toString();
}复制
@Slf4j
public class MyHandler {
/**
* 限流降级时调用
* @return
*/
public static String myBlockHandler(BlockException e){
log.info("限流了!!!!"+e.getMessage());
return "限流了!!!!";
}
/**
* 出错时调用
* @return
*/
public static String myFallBack(){
log.info("出错了!!!");
return "出错了!!!";
}
}复制
看一下效果,当QPS大于2时,会出现限流
更多详细教程见官方文档https://github.com/alibaba/Sentinel/wiki/
喜欢就加个关注吧,