如何使用Sentinel做限流、熔断和降级

2019-06-28 11:51:28 浏览数 (2)

1. 引入Sentinel依赖,在pom.xml中引入

代码语言:javascript复制
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.6.0</version>
</dependency>

2. 定义资源

资源 是Sentinel中的核心概念之一。在java中的资源一般是接口方法。例如,把需要控制流量的代码用 Sentinel API SphU.entry("HelloWorld") 和 entry.exit() 包围起来即可。在下面的例子中,我们将 System.out.println("hello world"); 作为资源(被保护的逻辑),用 API 包装起来。参考代码如下:

代码语言:javascript复制
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!");
	}
    }
}

也可以使用Sentinel提供的注解来定义资源,它是配合着aop或者aspectJ来使用的:

代码语言:javascript复制
@SentinelResource("HelloWorld")
public void helloWorld() {
    // 资源中的逻辑
    System.out.println("hello world");
}

也可以使用SphU.asyncEntry(xxx)异步调用资源。 上面的方式都可以定义资源。

3. 定义规则

规则主要有流控规则、降级规则、系统规则、权限规则、热点参数规则等:

代码语言:javascript复制
FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控规则
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降级规则
SystemRuleManager.loadRules(List<SystemRule> rules); // 修改系统规则
AuthorityRuleManager.loadRules(List<AuthorityRule> rules); // 修改授权规则

1. 流控规则

通过流控规则来指定允许该资源通过的请求次数,例如下面的代码定义了资源 HelloWorld 每秒最多只能通过 20 个请求。

代码语言:javascript复制
private static void initFlowRules(){
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule();
    rule.setResource("HelloWorld");
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    // Set limit QPS to 20.
    rule.setCount(20);
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

详情参考:流量控制 1

2. 熔断降级规则

熔断降级规则包含下面几个重要的属性: resource 资源名,即限流规则的作用对象 count 阈值 grade 降级模式,根据 RT 降级还是根据异常比例降级 RT timeWindow 降级的时间,单位为 s 同一个资源可以同时有多个降级规则。 理解上面规则的定义之后,我们可以通过调用 DegradeRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则。

代码语言:javascript复制
private void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    rule.setResource(KEY);
    // set threshold RT, 10 ms
    rule.setCount(10);
    rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
    rule.setTimeWindow(10);
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

我们通常用以下几种方式来衡量资源是否处于稳定的状态:

  • 平均响应时间 (DEGRADE_GRADE_RT):当资源的平均响应时间超过阈值(DegradeRule 中的 count,以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求(即 QPS >= 5),它们的 RT 都持续超过这个阈值,那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。
  • 异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。 详情参考:熔断降级 2

3. 系统保护规则 (SystemRule)

参数说明:

  • highestSystemLoad 最大的 load1,参考值 -1 (不生效)
  • avgRt 所有入口流量的平均响应时间 -1 (不生效)
  • maxThread 入口流量的最大并发数 -1 (不生效)
  • qps 所有入口资源的 QPS -1 (不生效)

硬编码的方式定义流量控制规则如下:

代码语言:javascript复制
private void initSystemRule() {
    List<SystemRule> rules = new ArrayList<>();
    SystemRule rule = new SystemRule();
    rule.setHighestSystemLoad(10);
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 和线程数四个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持四种阈值类型:

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

详情参考:系统自适应保护 3

4. 访问控制规则 (AuthorityRule)

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

调用方信息通过 ContextUtil.enter(resourceName, origin) 方法中的 origin 参数传入。

授权规则,即黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:

  • resource:资源名,即限流规则的作用对象
  • limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB
  • strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式 比如我们希望控制对资源 test 的访问设置白名单,只有来源为 appA 和 appB 的请求才可通过,则可以配置如下白名单规则:
代码语言:javascript复制
AuthorityRule rule = new AuthorityRule();
rule.setResource("test");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("appA,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));

详细信息参考:黑白名单控制 4

5. 热点规则 (ParamFlowRule)

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。 使用该规则需要引入依赖:
代码语言:javascript复制
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>x.y.z</version>
</dependency>

热点参数规则(ParamFlowRule)类似于流量控制规则(FlowRule):

  • resource 资源名,必填
  • count 限流阈值,必填
  • grade 限流模式 QPS 模式
  • paramIdx 热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置
  • paramFlowItemList 参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型

可以通过 ParamFlowRuleManager 的 loadRules 方法更新热点参数规则,下面是一个示例:

代码语言:javascript复制
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

4. 控制台工具

  1. 下载控制台 jar 包并在本地启动:可以参见 此处文档 6
  2. 客户端接入需要在pom中引入如下依赖:
代码语言:javascript复制
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.6.0</version>
</dependency>
  1. 启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口。更多的参数参见启动参数文档 7
  2. 确保应用端有访问量

在控制台上可以看到资源的实时流控数据,可以进行规则配置推送等。 控制台详情可参考:控制台启动及配置 8

5. 规则管理及推送

Sentinel 控制台同时提供简单的规则管理以及推送的功能。规则推送分为 3 种模式,包括 "原始模式"、"Pull 模式" 和"Push 模式"。

  • 原始模式即在控制台直接页面上配置,仅在内存生效,重启后规则会丢失。
  • pull模式数据源一般为本地文件、RDBMS等,一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。以本地文件数据源为例:
代码语言:javascript复制
public class FileDataSourceInit implements InitFunc {

    @Override
    public void init() throws Exception {
        String flowRulePath = "xxx";

        ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(
            flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
        );
        // 将可读数据源注册至 FlowRuleManager.
        FlowRuleManager.register2Property(ds.getProperty());

        WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);
        // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
        // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.
        WritableDataSourceRegistry.registerFlowDataSource(wds);
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

会定时轮询文件的变更,读取规则。好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性。首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。

  • 在生产环境推荐使用push模式,对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。可参考:在生产环境上使用 9

6. 其他功能

Sentinel还提供了集群流控、网关流控、实时监控、动态规则、主流框架适配(与servlet、dubbo、springcloud、grpc)和多语言生态等功能,具体详情见官方wiki,官方wiki地址 10

7. 生产环境注意事项

  • 启动sentinel core时需要注意的事项参见:启动配置项 11 需要设置project.name与控制台的ip和端口等;
  • 规则配置部分,参见:生产环境上使用方法 12
代码语言:javascript复制
 [1]: https://github.com/alibaba/Sentinel/wiki/流量控制
  [2]: https://github.com/alibaba/Sentinel/wiki/熔断降级
  [3]: https://github.com/alibaba/Sentinel/wiki/系统自适应限流
  [4]: https://github.com/alibaba/Sentinel/wiki/黑白名单控制
  [5]: https://github.com/alibaba/Sentinel/wiki/热点参数限流
  [6]: https://github.com/alibaba/Sentinel/wiki/控制台#2-启动控制台
  [7]: https://github.com/alibaba/Sentinel/wiki/控制台#32-配置启动参数
  [8]: https://github.com/alibaba/Sentinel/wiki/控制台#2-启动控制台
  [9]: https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel
  [10]: https://github.com/alibaba/Sentinel/wiki
  [11]: https://github.com/alibaba/Sentinel/wiki/启动配置项
  [12]: https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel

0 人点赞