sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_限流


Sentinel 是什么

什么是限流


  限流指的是限制系统或服务的请求流量,以保证系统的稳定性和可用性。在高并发场景下,系统容易出现请求量过大而导致资源瓶颈、响应变慢甚至崩溃的情况。

什么是Sentinel


  Sentinel是阿里巴巴开源的一款流量控制组件,主要用于分布式系统中的流量控制、熔断降级等功能。它提供了多种限流算法和限流维度,可以根据业务需求进行配置和使用,帮助开发者在高并发场景下有效地保护系统的稳定性。


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_java_02


为什么使用 Sentinel 限流


  在高并发场景下,系统容易出现请求量过大而导致资源瓶颈、响应变慢甚至崩溃的情况。为了保证系统的可用性和稳定性,需要对请求进行限流。

  • 保护系统稳定性:限制每个用户或服务的请求速率,可以有效地降低系统的负载,避免系统崩溃。
  • 提高服务可用性:限流可以避免系统因过度压力而出现响应延迟、错误甚至宕机等问题,提高服务的可用性和稳定性。
  • 防止资源被耗尽:在高并发的情况下,系统资源很容易被耗尽,例如数据库连接池、网络带宽等,通过限流可以避免资源被耗尽,提高系统的性能。
  • 防止恶意攻击:限流可以控制请求的频率,防止恶意攻击、爬虫等非法访问。

Sentinel 特点和优势



sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_Powered by 金山文档_03


  Sentinel具有以下几个主要特点:

  • 多种限流算法:包括令牌桶、漏桶等,可以根据业务场景选择合适的算法。
  • 多种限流维度:包括QPS、并发线程数、异常比例等,可以根据不同的维度来进行限流。
  • 熔断降级:可以在服务不可用或异常时快速切换到备用服务或默认返回值,保证系统的可用性。
  • 实时监控:提供了Dashboard可视化监控界面,可以实时查看系统的运行状况、设置规则、查看报表等。
  • 报警机制:可以在系统出现异常时自动发送邮件、短信等通知。

Sentinel 限流的原理和实现

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

  • 核心库(Java 客户端)
  • 控制台(Dashboard)


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_限流_04


  Sentinel 的核心骨架,将不同的 Slot 按照顺序串在一起(责任链模式)),从而将不同的功能(限流、降级、系统保护)组合在一起。下面主要就是介绍其中的数据统计与流量统计的实现。


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_java_05


配置实时刷新


   Sentinel Datasource 配置的规则可以动态实时调整。

  在应用启动阶段,程序会主动从 Sentinel Datasource 获取限流规则配置。而在运行期,我们也可以在 Sentinel 控制台动态修改限流规则,应用程序会实时监听配置中心的数据变化,进而获取变更后的数据。

Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel 客户端


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_流量控制_06


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_Powered by 金山文档_07


  大家有兴趣的可以就着这张图对照着源码来看


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_java_08


数据统计


  要做流量控制,首先,就要先进行统计,它要知道当前接口的 QPS 和并发是多少,进而判断是否限流。

  数据统计的代码在 StatisticNode 中,对于 QPS 数据,它使用了滑动窗口的设计。滑动窗口算法的核心类 LeapArray。

  滑动窗口算法的原理是在固定窗口中分割出多个小时间窗口,分别在每个小时间窗口中记录访问次数,然后根据时间将窗口往前滑动并删除过期的小时间窗口。最终只需要统计滑动窗口范围内的所有小时间窗口总的计数即可。


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_Powered by 金山文档_09


  将单位时间切成了多个窗口,每次计算 QPS 时,计算 当前窗口 + 过去几个窗口 的流量总和,这样就避免了固定窗口的问题 (具体使用几个窗口,取决于窗口大小和单位时间大小。例如上图,每个窗口大小为 500 ms,以 1 s 为单位时间做限流,每次使用 current + last 即可)

  对于 线程数 数据,它使用了线程计数器进行累加。

publicclassStatisticNodeimplementsNode {

    /**
     * 统计近一秒的数据, 按秒统计,分成两个窗口,每个窗口500ms,用来统计QPS
     * Holds statistics of the recent {@code INTERVAL} seconds. The {@code INTERVAL} is divided into time spans                       
     * by given {@code sampleCount}.
     */privatetransientvolatileMetricrollingCounterInSecond=newArrayMetric(SampleCountProperty.SAMPLE_COUNT,
            IntervalProperty.INTERVAL);

    /**
     * Holds statistics of the recent 60 seconds. The windowLengthInMs is deliberately set to 1000 milliseconds,
     * meaning each bucket per second, in this way we can get accurate statistics of each second.
     * 统计近一分钟的数据, 按分钟统计,分成60个窗口,每个窗口 1000ms
     */privatetransientMetricrollingCounterInMinute=newArrayMetric(60, 60 * 1000, false);

    /**
     * The counter for thread count. 当前并发线程数
     */privateLongAddercurThreadNum=newLongAdder();

    /**
     * The last timestamp when metrics were fetched.
     */privatelonglastFetchTime= -1;

}
复制代码

流量控制


  Sentinel的流量控制有两个维度的控制:并发线程数和QPS


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_限流_10


  它们都是针对某个具体的接口来设置的

   Sentinel 的限流原理主要是通过统计系统的QPS(即每秒请求数量)和并发量来控制系统的流量,从而达到限流的目的。当并发请求数量超过预设的阈值时,Sentinel 会拒绝该请求,并返回一个 FlowException 的错误响应。

  限流过滤的实现主要在 FlowSlot 的 entry 方法进行。FlowSlot 会根据预设的规则,结合StatisticSlot 统计出来的实时信息进行流量控制。

publicclassFlowSlotextendsAbstractLinkedProcessorSlot<DefaultNode> {

    @Overridepublicvoidentry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args)throws Throwable {
        //流控检查
        checkFlow(resourceWrapper, context, node, count, prioritized);
        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }

    voidcheckFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)throws BlockException {

        Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
        List<FlowRule> rules = flowRules.get(resource.getName());
        if (rules != null) {
            for (FlowRule rule : rules) {

                if (!canPassCheck(rule, context, node, count, prioritized)) {
                    thrownewFlowException(rule.getLimitApp(), rule);
                }
            }
        }
    }


    publicvoidcheckFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,
                          Context context, DefaultNode node, int count, boolean prioritized)throws BlockException {
        if (ruleProvider == null || resource == null) {
            return;
        }
        // 获取对应配置的流控规则
        Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
        if (rules != null) {
            for (FlowRule rule : rules) {
                //规则逐一校验,检查是否通过if (!canPassCheck(rule, context, node, count, prioritized)) {
                    thrownewFlowException(rule.getLimitApp(), rule);
                }
            }
        }
    }

}
复制代码

Sentinel 流量控制实践

引入依赖


<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId><version>2.1.2.RELEASE</version></dependency>复制代码

添加配置


spring:cloud:sentinel:transport:port:8719# 假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口,默认8719dashboard:http://localhost:8719# 指定控制台服务的地址filter:enabled:true# 心跳监测eager:true复制代码

启动控制台,添加配置


  控制台的规则默认存储在内存中,重启就会失效,如果需要持久化配置信息,可以参考官方文章配置(在生产环境中使用 Sentinel · alibaba/Sentinel Wiki · GitHub)


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_流量控制_11


测试


  返回对应的错误信息,当然具体的错误信息,大家可以自己根据项目需求去包装一下。


sentinel限流后不触发dubbo的mock逻辑 sentinel 限流_流量控制_12