跳到主要内容

1、Sentinel 快速实现限流

Sentinel简介

Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。

Sentinel 具有以下特征:

丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 的主要特性:

 

Sentinel 的开源生态:

 
Sentinel 和之前常用的熔断降级库 Netflix Hystrix 的对比
 

本地 Demo

1. 引入 Sentinel 依赖

如果您的应用使用了 Maven,则在 pom.xml 文件中加入以下代码即可:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.7.2</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 特性,自动 exit entry
    try (Entry entry = SphU.entry("HelloWorld")) {
   
     
        // 被保护的逻辑
        System.out.println("hello world");
	} catch (BlockException ex) {
   
     
            // 处理被流控的逻辑
	    System.out.println("blocked!");
	} finally {
   
     
        if (entry != null) {
   
     
            entry.exit();
        }
    }
  }
}

除了通过跑出异常的方式定义资源外,返回布尔值的方式也是一样的,这里不具体展开了。

PS:如果你不想对原有的业务代码进行侵入,也可以通过注解 SentinelResource 来进行资源埋点。

3. 定义规则

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

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);
}

我们需要对流控规则做个详细的了解,以便更好的进行限流的操作,流控的规则对应的是 FlowRule。

一条FlowRule有以下几个重要的属性组成:

  • resource: 规则的资源名
  • grade: 限流阈值类型,qps 或线程数
  • count: 限流的阈值
  • limitApp: 被限制的应用,授权时候为逗号分隔的应用集合,限流时为单个应用
  • strategy: 基于调用关系的流量控制
  • controlBehavior:流控策略

前三个属性比较好理解,最后三个比较难理解,让我们来详细看下最后三个属性:

limitApp

首先让我们来看下limitApp,从字面上看是指要限制哪个应用的意思,主要是用于根据调用方进行流量控制。
他有三种情况可以选择:

  • default

表示不区分调用者,来自任何调用者的请求都将进行限流统计。

  • {someoriginname}

表示针对特定的调用者,只有来自这个调用者的请求才会进行流量控制。

例如:资源 NodeA 配置了一条针对调用者 caller1 的规则,那么当且仅当来自 caller1 对 NodeA的请求才会触发流量控制。

  • other

表示除{someoriginname} 以外的其余调用方的流量进行流量控制。

例如:资源 NodeA 配置了一条针对调用者 caller1 的限流规则,同时又配置了一条调用者为 other 的规则,那么任意来自非 caller1 对 NodeA 的调用,都不能超过 other 这条规则定义的阈值。

strategy

基于调用关系的流量控制,也有三种情况可以选择:

  • STRATEGY_DIRECT

根据调用方进行限流。ContextUtil.enter(resourceName, origin) 方法中的 origin 参数标明了调用方的身份。

如果strategy 选择了DIRECT ,则还需要根据限流规则中的 limitApp 字段根据调用方在不同的场景中进行流量控制,包括有:”所有调用方“、”特定调用方origin“、”除特定调用方origin之外的调用方“。

  • STRATEGY_RELATE

根据关联流量限流。当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联,可使用关联限流来避免具有关联关系的资源之间过度的争抢。

比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。

举例来说:readdb 和 writedb 这两个资源分别代表数据库读写,我们可以给 readdb 设置限流规则来达到写优先的目的:设置 FlowRule.strategy 为 RuleConstant.STRATEGYRELATE,同时设置 FlowRule.refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。

  • STRATEGY_CHAIN

根据调用链路入口限流。假设来自入口 Entrance1 和 Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许根据某个入口的统计信息对资源进行限流。

举例来说:我们可以设置 FlowRule.strategy 为 RuleConstant.CHAIN,同时设置 FlowRule.refResource 为 Entrance1 来表示只有从入口 Entrance1 的调用才会记录到 NodeA 的限流统计当中,而对来自 Entrance2 的调用可以放行。

controlBehavior

流控策略,主要是发生拦截后具体的流量整形和控制策略,目前有三种策略,分别是:

  • CONTROLBEHAVIORDEFAULT

这种方式是:直接拒绝,该方式是默认的流量控制方式,当 qps 超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。

这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位。

  • CONTROLBEHAVIORWARM_UP

这种方式是:排队等待 ,又称为 冷启动。该方式主要用于当系统长期处于低水位的情况下,流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。

通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。

  • CONTROLBEHAVIORRATE_LIMITER

这种方式是:慢启动,又称为 匀速器模式。这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

具体的FlowRule 可以用下面这张图表示:

 

规则定义好了之后,启动应用后,就会自动对我们的业务代码进行保护了,当然实际生产环境中不可能通过硬编码的方式来定义规则的,sentinel 为我们提供了 DataSource 接口,通过实现该接口可以自定义规则的存储数据源。

通过DataSource 接口可以有很多种方式对规则进行持久化,例如:

  • 整合动态配置系统,如 ZooKeeper、Nacos 等,动态地实时刷新配置规则
  • 结合 RDBMS、NoSQL、VCS 等来实现该规则
  • 配合 Sentinel Dashboard 使用

本篇文章不对规则的持久化做具体的介绍,本篇文章主要是实现一个简单的限流的例子的接入。

PS:DateSource 接口在后期已经被拆成 ReadableDataSource 和 WritableDataSource 接口了。

4. 检查效果

Demo 运行之后,我们可以在日志 ~/logs/csp/${appName}-metrics.log.xxx 里看到下面的输出:

|--timestamp-|------date time----|--resource-|p |block|s |e|rt
1529998904000|2018-06-26 15:41:44|hello world|20|0    |20|0|0
1529998905000|2018-06-26 15:41:45|hello world|20|5579 |20|0|728
1529998906000|2018-06-26 15:41:46|hello world|20|15698|20|0|0
1529998907000|2018-06-26 15:41:47|hello world|20|19262|20|0|0
1529998908000|2018-06-26 15:41:48|hello world|20|19502|20|0|0
1529998909000|2018-06-26 15:41:49|hello world|20|18386|20|0|0

可以看到我们请求了很多次该资源后,sentinel 把每秒的统计信息都打印出来了,用 | 来分隔不同的参数,一共有8个参数,从左至右分别是: