一、Sentinel 的使用

Sentinel 的使用可以分为两个部分:

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。

核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果。

二、资源简介

资源Sentinel 中的核心概念之一。在上篇:Sentinel:分布式系统的流量防卫兵 | Spring Cloud 19 有过介绍。

资源可以是服务、服务里的方法、甚至是一段代码。

使用 Sentinel 来进行资源保护,主要分为几个步骤:

  • 定义资源
  • 定义规则
  • 检验规则是否生效

先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源

三、资源定义

3.1 方式一:主流框架的默认适配

为了减少开发的复杂程度,Sentinel对大部分的主流框架,例如:Web ServletDubboSpring CloudgRPCSpring WebFluxReactor 等都做了适配。您只需要引入对应的依赖即可方便地整合 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-resourcescatch 块中调用 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 注解定义资源并配置 blockHandlerfallback 函数来进行限流之后的处理。示例:

// 原本的业务方法.
@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 函数会针对所有类型的异常。请注意 blockHandlerfallback 函数的形式要求,更多指引可以参见 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):FlowqpsDemo

  • QPS流量控制(冷启动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 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 LoadCPU 使用率总体平均 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,appB
  • strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式

5.4.1 访问控制规则代码定义

比如我们希望控制对资源 test 的访问设置白名单,只有来源为 appAappB 的请求才可通过,则可以配置如下白名单规则:

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相关推荐

  1. 防止内卷和被潜规则,Spring Cloud Alibaba微服务架构实战派(上下册)|35岁程序员那些事

    目录 1 写书缘由 2 本书上册核心内容 2.1 Spring Cloud Alibaba基础实战 2.1.1 主要内容 2.1.2 MyBatis-Plus实现多租户架构的核心原理 2.2 分布式服 ...

  2. 使用Spring Security 资源服务器来保护Spring Cloud 微服务

    我在上一篇对资源服务器进行了简单的阐述,让大家对资源服务器的概念有了简单的认识,今天我将用实际例子来演示单体应用改造为Spring Cloud微服务时的资源服务器实现. 资源服务器改造 以Spring ...

  3. springcloud分布式事务_Spring Cloud学习资源一网打尽!Awesome Spring Cloud v1.0

    公正.公平.尊重原创.不夹带私人恩怨的Spring Cloud学习资源列表. TIPS: •本文链接较多,为了更好的阅读体验,建议翻到文章末尾,点击"扩展链接",排版相对好很多. ...

  4. Spring Cloud Alibba教程:Sentinel的使用

    点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 什么是Sentinel Sentinel,中文翻译为哨兵,是为微服务提供流量控制.熔断降级的功能 ...

  5. Spring Cloud Alibaba 实战 | 第十二篇: 微服务整合Sentinel的流控、熔断降级,赋能拥有降级功能的Feign新技能熔断,实现熔断降级双剑合璧(JMeter模拟测试)

    文章目录 一. Sentinel概念 1. 什么是Sentinel? 2. Sentinel功能特性 3. Sentinel VS Hystrix 二. Docker部署Sentinel Dashbo ...

  6. Spring Cloud Alibaba配置实例nacos+sentinel+dubbo实行服务注册、配置中心、熔断限流

    通过Spring Cloud Alibaba相关组件nacos+sentinel+dubbo实行服务注册.配置中心.熔断限流等功能 1.本机安装nacos和sentinel-dashboard服务端 ...

  7. Spring Cloud GateWay系列(三):路由规则动态刷新

    Spring Cloud Gateway旨在提供一种简单而有效的方式来路由API,并为它们提供横切关注点,例如:安全性.监控/指标和弹性.Route(路由)是网关的基本单元,由唯一标识符ID.目标地址 ...

  8. Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 最近管点闲事浪费了不少时间,感谢网友们的留言提醒. 及时纠正路线,继续跟大家一起学习Spring Cl ...

  9. 进击的 Spring Cloud Alibaba —— 框架与服务

    作者 | 陈曦(良名)  Spring Cloud Alibaba 项目成员,start.aliyun.com 负责人. 导读:本文整理自作者于 2020 年云原生微服务大会上的分享<进击的 S ...

最新文章

  1. Android Shape 的使用
  2. 写有效率的SQL查询(V)
  3. 内部网站更换服务器,网站更换服务器的具体操作流程!
  4. 垃圾代码还能出圈?手把手教你写垃圾代码,从入门到精通!
  5. Mountain Lion 10.8
  6. python-图像金字塔
  7. jsp嵌入vlc视频回放_【知识】如何用监控进行视频直播?一文了解清楚
  8. 网易云音乐云盘上传歌词的方法
  9. (15)FPGA与CPU区别
  10. LEARNING ACTIONABLE REPRESENTATIONS WITH GOAL-CONDITIONED POLICIES
  11. osg qt 三维模型加载
  12. 遐想:Android Nexus One Flan
  13. 虚拟机安装Ubuntu后的问题(不能全屏、不能上网;换源挂代理;安装搜狗输入法;pycharm的sudo模式启动快捷方式)
  14. treecnt 51Nod - 1677
  15. GITC2016花落上海,五大亮点抢先看
  16. C# .NET Core获取类属性/属性值,是否有属性/属性值,获取实体层描述,枚举描述,枚举英文获取枚举描述[Description(“xxx“)]
  17. SDL库的安装及游戏测试
  18. Oriented Fast神奇高效的代码实现方式——ORBSLAM2源码讲解(二)
  19. 解决方案|在线自习室
  20. 力控关节机器人(关节扭矩传感器力控)

热门文章

  1. ajax局部刷新不出来,关于Ajax局部刷新未出来数据的问题
  2. gRPC学习之六:gRPC-Gateway集成swagger
  3. <马哲>生产力和生产关系的辩证原理
  4. wait millis 60010, active 20, maxActive 20 处理 com.alibaba.druid.pool.GetConnectionTimeout
  5. 如何在3分钟内安装和使用Photoshop笔刷
  6. 分布式数据库实战第一节 分布式数据库的前世今生
  7. 数学分析讲义习题解答:(三:第一部分)
  8. 汽车数字钥匙设计02--UWB基础知识
  9. 处理迟到问题--简单的Java程序记录
  10. 小仙女讲InnoDB(6)——综述