Sentinel:资源与规则定义 | Spring Cloud 20
一、Sentinel 的使用
Sentinel
的使用可以分为两个部分:
- 核心库(
Java
客户端):不依赖任何框架/库,能够运行于Java 8
及以上的版本的运行时环境,同时对Dubbo / Spring Cloud
等框架也有较好的支持。 - 控制台(
Dashboard
):Dashboard
主要负责管理推送规则、监控、管理机器信息等。
核心库不依赖 Dashboard
,但是结合 Dashboard
可以取得最好的效果。
二、资源简介
资源 是 Sentinel
中的核心概念之一。在上篇:Sentinel:分布式系统的流量防卫兵 | Spring Cloud 19 有过介绍。
资源可以是服务、服务里的方法、甚至是一段代码。
使用 Sentinel
来进行资源保护,主要分为几个步骤:
- 定义资源
- 定义规则
- 检验规则是否生效
先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。
三、资源定义
3.1 方式一:主流框架的默认适配
为了减少开发的复杂程度,Sentinel
对大部分的主流框架,例如:Web Servlet
、Dubbo
、Spring Cloud
、gRPC
、Spring WebFlux
、Reactor
等都做了适配。您只需要引入对应的依赖即可方便地整合 Sentinel
。可以参见:主流框架的适配。
3.2 方式二:抛出异常的方式定义资源
SphU
提供 try-catch
风格的 API
。用这种方式,当资源发生了限流之后会抛出 BlockException
。这个时候可以捕捉异常,进行限流之后的逻辑处理。示例代码如下:
// 1.5.0 版本开始可以利用 try-with-resources 特性
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("自定义资源名")) {// 被保护的业务逻辑// do something here...
} catch (BlockException ex) {// 资源访问阻止,被限流或被降级// 在此处进行相应的处理操作
}
若
entry
的时候传入了热点参数,那么exit
的时候也一定要带上对应的参数(exit(count, args)
),否则可能会有统计错误。这个时候不能使用try-with-resources
的方式。另外通过Tracer.trace(ex)
来统计异常信息时,由于try-with-resources
语法中catch
调用顺序的问题,会导致无法正确统计异常数,因此统计异常信息时也不能在try-with-resources
的catch
块中调用Tracer.trace(ex)
。
1.5.0
之前的版本的示例:
Entry entry = null;
// 务必保证finally会被执行
try {// 资源名可使用任意有业务语义的字符串entry = SphU.entry("自定义资源名");// 被保护的业务逻辑// do something...
} catch (BlockException e1) {// 资源访问阻止,被限流或被降级// 进行相应的处理操作
} finally {if (entry != null) {entry.exit();}
}
SphU.entry(xxx)
需要与entry.exit()
方法成对出现,匹配调用,否则会导致调用链记录异常,抛出ErrorEntryFreeException
异常。
3.3 方式三:返回布尔值方式定义资源
SphO
提供 if-else
风格的 API。用这种方式,当资源发生了限流之后会返回 false
,这个时候可以根据返回值,进行限流之后的逻辑处理。示例代码如下:
// 资源名可使用任意有业务语义的字符串
if (SphO.entry("自定义资源名")) {// 务必保证finally会被执行try {/*** 被保护的业务逻辑*/} finally {SphO.exit();}
} else {// 资源访问阻止,被限流或被降级// 进行相应的处理操作
}
3.4 方式四:注解方式定义资源
Sentinel
支持通过 @SentinelResource
注解定义资源并配置 blockHandler
和 fallback
函数来进行限流之后的处理。示例:
// 原本的业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {throw new RuntimeException("getUserById command failed");
}// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {return new User("admin");
}
blockHandler
函数会在原方法被限流/降级/系统保护的时候调用,而fallback
函数会针对所有类型的异常。请注意blockHandler
和fallback
函数的形式要求,更多指引可以参见 Sentinel 注解支持文档。
3.5 方式五:异步调用支持
Sentinel
支持异步调用链路的统计。在异步调用中,需要通过 SphU.asyncEntry(xxx)
方法定义资源,并通常需要在异步的回调函数中调用 exit
方法。以下是一个简单的示例:
try {AsyncEntry entry = SphU.asyncEntry(resourceName);// 异步调用.doAsync(userId, result -> {try {// 在此处处理异步调用的结果.} finally {// 在回调结束后 exit.entry.exit();}});
} catch (BlockException ex) {// Request blocked.// Handle the exception (e.g. retry or fallback).
}
SphU.asyncEntry(xxx)
不会影响当前(调用线程)的 Context
,因此以下两个 entry
在调用链上是平级关系(处于同一层),而不是嵌套关系:
asyncEntry = SphU.asyncEntry(asyncResource);
entry = SphU.entry(normalResource);
// 调用链类似于:
// -parent
// ---asyncResource
// ---syncResource
若在异步回调中需要嵌套其它的资源调用(无论是 entry
还是 asyncEntry
),只需要借助 Sentinel
提供的上下文切换功能,在对应的地方通过 ContextUtil.runOnContext(context, f)
进行 Context
变换,将对应资源调用处的 Context
切换为生成的异步 Context
,即可维持正确的调用链路关系。示例如下:
public void handleResult(String result) {Entry entry = null;try {entry = SphU.entry("handleResultForAsync");// Handle your result here.} catch (BlockException ex) {// Blocked for the result handler.} finally {if (entry != null) {entry.exit();}}
}public void someAsync() {try {AsyncEntry entry = SphU.asyncEntry(resourceName);// Asynchronous invocation.doAsync(userId, result -> {// 在异步回调中进行上下文变换,通过 AsyncEntry 的 getAsyncContext 方法获取异步 ContextContextUtil.runOnContext(entry.getAsyncContext(), () -> {try {// 此处嵌套正常的资源调用.handleResult(result);} finally {entry.exit();}});});} catch (BlockException ex) {// Request blocked.// Handle the exception (e.g. retry or fallback).}
}
此时的调用链就类似于:
-parent
---asyncInvocation
-----handleResultForAsync
以下示例里面包含了普通资源与异步资源之间的各种嵌套示例:
package com.jztx.gm.demo;import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;import com.alibaba.csp.sentinel.AsyncEntry;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;public class AsyncEntryDemo {/*** 定义个一次的方法* @param arg* @param handler*/private void invoke(String arg, Consumer<String> handler) {CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(3);String resp = arg + ": " + System.currentTimeMillis();handler.accept(resp);} catch (Exception ex) {ex.printStackTrace();}});}/*** 定义异步调用资源,上级为test-async异步调用,下级为test-another-sync-in-async同步调用*/private void anotherAsync() {try {final AsyncEntry entry = SphU.asyncEntry("test-another-async");CompletableFuture.runAsync(() -> {ContextUtil.runOnContext(entry.getAsyncContext(), () -> {try {TimeUnit.SECONDS.sleep(2);anotherSyncInAsync();System.out.println("Async result: 666");} catch (InterruptedException e) {} finally {entry.exit();}});});} catch (BlockException ex) {ex.printStackTrace();}}/*** 定义一个同步调用资源,此时与test-async异步调用链路同级*/private void fetchSync() {Entry entry = null;try {entry = SphU.entry("test-sync");} catch (BlockException ex) {ex.printStackTrace();} finally {if (entry != null) {entry.exit();}}}/*** 定义同步调用资源,是test-async调用链路的下一级*/private void fetchSyncInAsync() {Entry entry = null;try {entry = SphU.entry("test-sync-in-async");} catch (BlockException ex) {ex.printStackTrace();} finally {if (entry != null) {entry.exit();}}}/*** 定义同步调用资源,是test-another-async调用链路的下一级*/private void anotherSyncInAsync() {Entry entry = null;try {entry = SphU.entry("test-another-sync-in-async");} catch (BlockException ex) {ex.printStackTrace();} finally {if (entry != null) {entry.exit();}}}/*** 定义一层异步调用资源test-async,同时定义一层同步调用资源test-sync,此时test-async和test-async调用链路同级*/private void doAsyncThenSync() {try {final AsyncEntry entry = SphU.asyncEntry("test-async");this.invoke("abc", resp -> {ContextUtil.runOnContext(entry.getAsyncContext(), () -> {try {for (int i = 0; i < 7; i++) {anotherAsync();}System.out.println(resp);fetchSyncInAsync();} finally {entry.exit();}});});fetchSync();} catch (BlockException ex) {ex.printStackTrace();}}public static void main(String[] args) throws Exception {initFlowRule();AsyncEntryDemo service = new AsyncEntryDemo();// Expected invocation chain://// EntranceNode: machine-root// -EntranceNode: async-context// --test-top// ---test-sync// ---test-async// ----test-another-async// -----test-another-sync-in-async// ----test-sync-in-async// 静态方法用于标识调用链路入口// 其中 contextName 代表调用链路入口名称(上下文名称),origin 代表调用来源名称。默认调用来源为空。// 仅在当前线程的初次调用生效,后面再调用不会覆盖当前线程的调用链路,直到 ContextUtil.exit()。ContextUtil.enter("async-context", "originA");Entry entry = null;try {// 定义一层同步调用entry = SphU.entry("test-top");System.out.println("Do something...");// 在test-top的下一层为service.doAsyncThenSync();} catch (BlockException ex) {// Request blocked, handle the exception.ex.printStackTrace();} finally {if (entry != null) {entry.exit();}ContextUtil.exit();}TimeUnit.SECONDS.sleep(20);}/*** 初始化规则*/private static void initFlowRule() {// Rule 1 won't take effect as the limitApp doesn't match.FlowRule rule1 = new FlowRule().setResource("test-another-sync-in-async").setLimitApp("originB").as(FlowRule.class).setCount(4).setGrade(RuleConstant.FLOW_GRADE_QPS);// Rule 2 will take effect.FlowRule rule2 = new FlowRule().setResource("test-another-async").setLimitApp("default").as(FlowRule.class).setCount(5).setGrade(RuleConstant.FLOW_GRADE_QPS);List<FlowRule> ruleList = Arrays.asList(rule1, rule2);FlowRuleManager.loadRules(ruleList);}
}
四、规则简介
围绕资源的实时状态设定的规则,可以包括:流量控制规则、熔断降级规则以及系统保护规则。
Sentinel
的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel
也提供相关 API
,供您来定制自己的规则策略。
Sentinel
支持以下几种规则:
- 流量控制规则
- 熔断降级规则
- 系统保护规则
- 来源访问控制规则
- 热点参数规则
- 网关流量控制
五、规则定义
5.1 流量控制规则 (FlowRule)
流量规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 或线程数模式 | QPS 模式 |
limitApp | 流控针对的调用来源 | default,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流 | 直接拒绝 |
同一个资源可以同时有多个限流规则。
5.1.1 流量控制规则代码定义
理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules()
方法来用硬编码的方式定义流量控制规则,比如:
private static void initFlowQpsRule() {List<FlowRule> rules = new ArrayList<>();FlowRule rule1 = new FlowRule();rule1.setResource(resource);// Set max qps to 20rule1.setCount(20);rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);rule1.setLimitApp("default");rules.add(rule1);FlowRuleManager.loadRules(rules);
}
5.1.2 流量控制规则完整示例
详细示例请参考:
并发线程数流量控制:ThreadDemo
QPS流量控制(直接拒绝
RuleConstant.CONTROL_BEHAVIOR_DEFAULT
):FlowqpsDemoQPS流量控制(冷启动
RuleConstant.CONTROL_BEHAVIOR_WARM_UP
):WarmUpFlowDemo
流量控制规则更多参考:https://sentinelguard.io/zh-cn/docs/flow-control.html
5.2 熔断降级规则 (DegradeRule)
熔断降级规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
同一个资源可以同时有多个降级规则。
5.2.1 熔断降级规则代码定义
private static void initDegradeRule() {List<DegradeRule> rules = new ArrayList<>();DegradeRule rule = new DegradeRule(resource);.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());.setCount(0.7); // Threshold is 70% error ratio.setMinRequestAmount(100).setStatIntervalMs(30000) // 30s.setTimeWindow(10);rules.add(rule);DegradeRuleManager.loadRules(rules);
}
5.2.2 熔断降级规则完整示例
详细示例请参考:SlowRatioCircuitBreakerDemo
熔断降级规则更多参考:https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
5.3 系统保护规则 (SystemRule)
Sentinel
系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load
、CPU 使用率
、总体平均 RT
、入口 QPS
和并发线程数
等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
highestSystemLoad | load1 触发值,用于触发自适应控制阶段 | -1 (不生效) |
avgRt | 所有入口流量的平均响应时间 | -1 (不生效) |
maxThread | 入口流量的最大并发数 | -1 (不生效) |
qps | 所有入口资源的 QPS | -1 (不生效) |
highestCpuUsage | 当前系统的 CPU 使用率(0.0-1.0) | -1 (不生效) |
5.3.1 系统保护规则代码定义系
private void initSystemProtectionRule() {List<SystemRule> rules = new ArrayList<>();SystemRule rule = new SystemRule();rule.setHighestSystemLoad(10);rules.add(rule);SystemRuleManager.loadRules(rules);
}
5.3.2 系统保护规则完整示例
详细示例请参考:SystemGuardDemo
系统保护规则更多参考:https://sentinelguard.io/zh-cn/docs/system-adaptive-protection.html
5.4 访问控制规则 (AuthorityRule)
很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel
的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
调用方信息通过
ContextUtil.enter(resourceName, origin)
方法中的origin
参数传入。
授权规则,即黑白名单规则(AuthorityRule
)非常简单,主要有以下配置项:
resource
:资源名,即限流规则的作用对象limitApp
:对应的黑名单/白名单,不同origin
用,
分隔,如: appA,appBstrategy
:限制模式,AUTHORITY_WHITE
为白名单模式,AUTHORITY_BLACK
为黑名单模式,默认为白名单模式
5.4.1 访问控制规则代码定义
比如我们希望控制对资源 test
的访问设置白名单,只有来源为 appA
和 appB
的请求才可通过,则可以配置如下白名单规则:
AuthorityRule rule = new AuthorityRule();
rule.setResource("test");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("appA,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
5.4.2 访问控制规则完整示例
详细示例请参考:AuthorityDemo
访问控制规则更多参考:https://sentinelguard.io/zh-cn/docs/origin-authority-control.html
5.5 热点规则 (ParamFlowRule)
热点参数规则(ParamFlowRule
)类似于流量控制规则(FlowRule
):
属性 | 说明 | 默认值 |
---|---|---|
resource | 资源名,必填 | |
count | 限流阈值,必填 | |
grade | 限流模式 | QPS 模式 |
durationInSec | 统计窗口时间长度(单位为秒),1.6.0 版本开始支持 | 1s |
controlBehavior | 流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持 | 快速失败 |
maxQueueingTimeMs | 最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本开始支持 | 0ms |
paramIdx | 热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置 | |
paramFlowItemList | 参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型 | |
clusterMode | 是否是集群参数流控规则 | false |
clusterConfig | 集群流控相关配置 |
5.5.1 热点规则代码定义
ParamFlowRule rule = new ParamFlowRule(resourceName).setParamIdx(0).setCount(5);
// 针对 int 类型的参数 PARAM_B,单独设置限流 QPS 阈值为 10,而不是全局的阈值 5.
ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(PARAM_B)).setClassType(int.class.getName()).setCount(10);
rule.setParamFlowItemList(Collections.singletonList(item));ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
5.5.2 热点规则完整示例
详细示例请参考: sentinel-demo-parameter-flow-control
热点规则更多参考:https://sentinelguard.io/zh-cn/docs/parameter-flow-control.html
Sentinel:资源与规则定义 | Spring Cloud 20相关推荐
- 防止内卷和被潜规则,Spring Cloud Alibaba微服务架构实战派(上下册)|35岁程序员那些事
目录 1 写书缘由 2 本书上册核心内容 2.1 Spring Cloud Alibaba基础实战 2.1.1 主要内容 2.1.2 MyBatis-Plus实现多租户架构的核心原理 2.2 分布式服 ...
- 使用Spring Security 资源服务器来保护Spring Cloud 微服务
我在上一篇对资源服务器进行了简单的阐述,让大家对资源服务器的概念有了简单的认识,今天我将用实际例子来演示单体应用改造为Spring Cloud微服务时的资源服务器实现. 资源服务器改造 以Spring ...
- springcloud分布式事务_Spring Cloud学习资源一网打尽!Awesome Spring Cloud v1.0
公正.公平.尊重原创.不夹带私人恩怨的Spring Cloud学习资源列表. TIPS: •本文链接较多,为了更好的阅读体验,建议翻到文章末尾,点击"扩展链接",排版相对好很多. ...
- Spring Cloud Alibba教程:Sentinel的使用
点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 什么是Sentinel Sentinel,中文翻译为哨兵,是为微服务提供流量控制.熔断降级的功能 ...
- Spring Cloud Alibaba 实战 | 第十二篇: 微服务整合Sentinel的流控、熔断降级,赋能拥有降级功能的Feign新技能熔断,实现熔断降级双剑合璧(JMeter模拟测试)
文章目录 一. Sentinel概念 1. 什么是Sentinel? 2. Sentinel功能特性 3. Sentinel VS Hystrix 二. Docker部署Sentinel Dashbo ...
- Spring Cloud Alibaba配置实例nacos+sentinel+dubbo实行服务注册、配置中心、熔断限流
通过Spring Cloud Alibaba相关组件nacos+sentinel+dubbo实行服务注册.配置中心.熔断限流等功能 1.本机安装nacos和sentinel-dashboard服务端 ...
- Spring Cloud GateWay系列(三):路由规则动态刷新
Spring Cloud Gateway旨在提供一种简单而有效的方式来路由API,并为它们提供横切关注点,例如:安全性.监控/指标和弹性.Route(路由)是网关的基本单元,由唯一标识符ID.目标地址 ...
- Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流
点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 最近管点闲事浪费了不少时间,感谢网友们的留言提醒. 及时纠正路线,继续跟大家一起学习Spring Cl ...
- 进击的 Spring Cloud Alibaba —— 框架与服务
作者 | 陈曦(良名) Spring Cloud Alibaba 项目成员,start.aliyun.com 负责人. 导读:本文整理自作者于 2020 年云原生微服务大会上的分享<进击的 S ...
最新文章
- Android Shape 的使用
- 写有效率的SQL查询(V)
- 内部网站更换服务器,网站更换服务器的具体操作流程!
- 垃圾代码还能出圈?手把手教你写垃圾代码,从入门到精通!
- Mountain Lion 10.8
- python-图像金字塔
- jsp嵌入vlc视频回放_【知识】如何用监控进行视频直播?一文了解清楚
- 网易云音乐云盘上传歌词的方法
- (15)FPGA与CPU区别
- LEARNING ACTIONABLE REPRESENTATIONS WITH GOAL-CONDITIONED POLICIES
- osg qt 三维模型加载
- 遐想:Android Nexus One Flan
- 虚拟机安装Ubuntu后的问题(不能全屏、不能上网;换源挂代理;安装搜狗输入法;pycharm的sudo模式启动快捷方式)
- treecnt 51Nod - 1677
- GITC2016花落上海,五大亮点抢先看
- C# .NET Core获取类属性/属性值,是否有属性/属性值,获取实体层描述,枚举描述,枚举英文获取枚举描述[Description(“xxx“)]
- SDL库的安装及游戏测试
- Oriented Fast神奇高效的代码实现方式——ORBSLAM2源码讲解(二)
- 解决方案|在线自习室
- 力控关节机器人(关节扭矩传感器力控)
热门文章
- ajax局部刷新不出来,关于Ajax局部刷新未出来数据的问题
- gRPC学习之六:gRPC-Gateway集成swagger
- <马哲>生产力和生产关系的辩证原理
- wait millis 60010, active 20, maxActive 20 处理 com.alibaba.druid.pool.GetConnectionTimeout
- 如何在3分钟内安装和使用Photoshop笔刷
- 分布式数据库实战第一节 分布式数据库的前世今生
- 数学分析讲义习题解答:(三:第一部分)
- 汽车数字钥匙设计02--UWB基础知识
- 处理迟到问题--简单的Java程序记录
- 小仙女讲InnoDB(6)——综述