Alibaba Sentinel熔断降级/限流框架不完全解析

关于Alibaba Sentinel熔断降级/限流框架

阿里在集群防御、熔断限流方向所做可圈可点。 其中 github: Sentinel 在其中扮演着很重要的角色。 相对于类似的框架如 Hystrix(多得多的文档), 更加符合中国互联网工程师的思维习惯。

接下来的内容如果觉得啰嗦可以直接跳到Sentinel架构介绍小节

类似的防御体系:

  1. 美团的github: OCTO更像是集全公司的力量, 由上而下的要求全公司接入的、集中治理的平台。
  2. 滴滴对熔断、限流上更多的每个独立的团队自行发展, 各成体系。

以及其他企业的预案方案,想要轻松接入都不容易。 想要最低成本的接入熔断、限流,Sentinel更合适。

本文将持续迭代。

这里有对熔断、限流的包含源码解析的详解: Alibaba Sentinel 限流、熔断实现详解

一段话的介绍

预案

对可预期的问题, 例如 依赖的某服务不可用网络可能不稳定DB服务器不稳定下星期要进行大规模抢购可能有人恶意爬取 等这些不符合当前业务运行规则的。
设计一个处理方案, 来规避这些问题。

一般处理方案(简单列举):

  • 两地三中心
  • 服务器紧急扩容(参考微博)
  • 代码中添加if else语句切换请求来源
  • ···

通过建立一些针对可预期的问题的处理方案, 最低限度的保证自己系统的主体服务可用性

限流

通过压测测试出当前系统的最大负载能力,然后配置超过这个负载能力的请求全部丢弃、或者排队等待。
在长链路调用中,以最低承受单点为基准, 超过这个单点的负载能力的请求全部丢弃、或者排队等待。

通过限制调用方对自己的调用,起到保护自己系统的效果

熔断
在Sentinel中, 熔断往往跟降级靠在一起(我更加认为降级本就是预案的一部分,限流是预案、熔断也是预案,熔断却不一定都是降级)。

所有的对外部的调用都认为是不可控的。
其中部分调用是必须依赖的, 部分调用是可以被替代的(如查缓存改查DB)。

当我们对外部的请求发生异常, 例如超时、抛出异常, 且超过一定的阈值。这个时候我们可以理解为外部依赖不可用,多次请求也只是在浪费时间。这个时候可以禁止访问外部、或者直接返回默认值。

通过限制自己对外部系统的调用, 起到节约响应时间、维护链路稳定的作用

Alibaba Sentinel建立的用处就是在针对服务上的熔断与限流。此Sentinel不同于Redis的Sentinel
它提供一个客户端SDK, 通过编写熔断、限流规则, 起到熔断、限流的作用。
同时它提供一个服务端(非必须), 能够更好的监控客户端运行状态,同时免编码的直接下发规则给客户端。

我基于Sentinel做的一个对Hello World的限流(看不到文件可直接下载):

Sentinel使用方式

部署Sentinel的客户端

参考Github介绍 Sentinel新手指南 以及 如何使用

大致这么几部:

  1. 引入依赖(pom/sentinel-core.jar
  2. 代码编写植入
  3. 定义规则(支持本地配置规则, 使用Sentinel Dashboard配置规则)。

相对来说Sentinel的接入非常轻便简单了。但是遇到每一段代码都植入Sentinel给定的代码, 也是很麻烦, 因此可以参考小节 基于Sentinel所做易用性拓展

参考官方demo:

// 代码编写植入
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 Dashboard配置规则)。
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);
}

部署Sentinel的服务端

服务端非必须, 主要提供规则配置和下发。 对简化编码有很大的帮助。

参考Github介绍 Sentinel 控制台

服务端UI功能:

名称概述用法其他
实时监控采集当前资源的限流信息观察
簇点链路把代码中植入的资源上报统一熔断、限流自动上报功能,很棒
流控规则限流根据资源, 支持访问资源的QPS和访问资源的线程数限流一般是限制 QPS
降级规则熔断降级根据资源,请求超时、异常、数超阈值熔断异于hystrix,时间窗口后直接恢复
热点规则未知未知未知
系统规则未知未知未知
授权规则未知未知未知
集群流控见名知意--
机器列表见名知意--

Sentinel架构介绍

对Sentinel的介绍, 仅限于客户端(服务端并不是必须的产品)。参考源码 Sentinelsentinel-core
Sentinel中的核心概念 resource(资源), 定义为被Sentinel保护的所有的事物。
所有的预案操作都是依据资源而定的。

sentinel-core直观代码分层:

➜  sentinel-core git:(master) tree -d
.
└── src
    └── main
       └── java
          └── com
              └── alibaba
                  └── csp
                      └── sentinel
                          ├── annotation
                          ├── cluster
                          │   ├── client
                          │   ├── log
                          │   └── server
                          ├── concurrent
                          ├── config
                          ├── context
                          ├── eagleeye
                          ├── init
                          ├── log
                          ├── metric
                          │   └── extension
                          │       └── callback
                          ├── node
                          │   └── metric
                          ├── property
                          ├── slotchain
                          ├── slots
                          │   ├── block
                          │   │   ├── authority
                          │   │   ├── degrade
                          │   │   └── flow
                          │   │       └── controller
                          │   ├── clusterbuilder
                          │   ├── logger
                          │   ├── nodeselector
                          │   ├── statistic
                          │   │   ├── base
                          │   │   ├── data
                          │   │   └── metric
                          │   │       └── occupy
                          │   └── system
                          ├── spi
                          └── util
                              └── function

如上树形包结构, 重点关注这几个包:

包名概述其他
cluster在客户端自成集群中 限流生效后续专门讲
node数据节点, 存储SDK基于Resource运行时的包括RT、异常数等数据-
solt插件,限流、熔断、监控都是基于插件来的非常重要
slotchain每次对资源的调用都需要走一次插件责任链式solt
context上下文载体基于 ThreadLocal

Sentinel Chain

Sentinel 以插件的形式,将功能打包在客户端SDK中。
插件可以构成一条单链(责任链模式)。限流、熔断、日志等功能, 都被打包在链条中。

其中的每一个节点, 就是 solt
详见com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot
通过next指向下一个需要处理的节点。

通常用到的solt

概述其他
NodeSelectorSlot维护一条调用链(Sentinel代码嵌套调用)。
链首维护着一个DefaultNode
构成其它信息树的根节点。
这个类很重要,但是也很简单。就是维护一个调用树。
ClusterBuilderSlot类似NodeSelectorSlot,维护的是ClusterNode
存储着调用次数、线程数等信息。
主要用于限流处FlowSlot
-
LogSlot在后续插件抛异常之后记一笔记录异常
StatisticSlot在统计信息中记一笔后续熔点、限流数据的的来源
SystemSlot基于整个应用的Load、QPS等限制-
AuthoritySlot映射处理规则中的黑白名单-
FlowSlot限流控制可直接参阅源码
DegradeSlot熔断控制可直接参阅源码

Node

看接口定义, 它就是一个存储程序运行时的节点。
com.alibaba.csp.sentinel.node.Node

节点作用
StatisticNode资源统计(计算规则), 其它Node的父类。 solt依赖StatisticNode持有的数据工作
ClusterNode该节点中保存了资源的总体的运行时统计信息,包括rt,线程数,qps等等,
相同的资源会全局共享同一个ClusterNode,以便用于全局QPS、线程数限制
DefaultNode该节点持有指定上下文中指定资源的统计信息(NodeSelectorSlot创建),
当在同一个上下文(嵌套Sentinel)中次调用entry方法时,该节点可能会创建有一系列的子节点。
每个DefaultNode中会关联一个ClusterNode
他们俩的区别在于DefaultNode更多的关注于当前Context的实时指标,
ClusterNode更多的关注当前资源在所有的Context的指标(主要用这个
EntranceNode该节点在创建context的时候即被创建出来, 在一次调用链上下文中往往是头部节点

Sentinel执行流程图(我是重点,点我

针对 Sentinel 所推荐的基础语法, 对其实现细致做解剖分析:

    try {
	    entry = SphU.entry("HelloWorld");
        // 资源中的逻辑.
        System.out.println("hello world");
	} catch (BlockException e1) {
	    System.out.println("blocked!");
	} finally {
	   if (entry != null) {
	       entry.exit();
	   }
	}
Created with Raphaël 2.2.0 进入被包围代码块 获取上下文Context 处于嵌套代码中? ThreadLocal 获得Context 构建任务链ProcessorSlot Chain 1.NodeSelectorSlot 2.ClusterBuilderSlot 3.LogSlot 4.StatisticSlot 5.SystemSlot 6.AuthoritySlot 7.FlowSlot 8.DegradeSlot 依据链条逐个判断处理。 不符合规则则直接抛出 BlockException 在finally中逐个执行每个Solt的exit逻辑 受保护逻辑完毕 创建Context 构建EntranceNode链首 yes no

整体的逻辑相对简单。是对一个责任链的调用
重点在于整体数据链路的串联, 以及每个Solt的处理细则。

功能实现解析

限流功能

Sentinel的限流实现比较简单, 支持集群限流(单讲)和单机限流。实现方式委托给了包com.alibaba.csp.sentinel.slots.block.flow.controller 下的实现。 分别支持快速失败(DefaultController)、Warm up(热加载限流, WarmUpController)、排队等待(RateLimiterController)三种模式。
.
它隶属于FlowSlot插件


快速失败模式下的限流实现很简单, 通过获取ClusterNode中记录的当前每秒钟通过的请求数(clusterNode), 加上当前请求需要的请求数(一般是1), 如果大于规则的设定值, 直接抛出异常,请求丢弃。 被限流。

Warm up 下的实现类似于Guava的限流实现, 采取 令牌桶 的方式。

  1. 从配置好规则开始, 限流大小从0逐步增长至预设限流大小。
  2. 每次执行限流判断的时候, 从令牌桶中重新判断是否需要灌入令牌。
  3. 快速失败模式一样, 如果已经消费的令牌数加上需要的令牌数多于可取令牌数。 不被限流。

排队等待 模式很显然, 一般情况都会想到漏桶算法。 参见 Github 匀速器

  1. 排队等待采取了一种猜测等待时间的方法。
  2. 如果一个请求来了, 需要排队, 若预计等待时间过了依旧不满足限流条件, 自然放行。
  3. 而预计等待时间在限流条件下, 还会等待超时, 则自然直接拒绝掉。否则休眠之后放行。
  4. 排队等待的将支持后续排队, 被限流的程序则不再会被执行。

想看详细的介绍?
这里有包含源码解析的详解: Alibaba Sentinel 限流、熔断实现详解

熔断功能

熔断功能的实现就更简单了。
熔断提供三种熔断模式:RT(超时时间), 异常比例(异常数在所有请求数中的占比),异常数(具体阈值)
.
它隶属于DegradeSlot插件
它也都是依据于 ClusterNode 提供的信息做的熔断。


RT 模式 还在写。

想看详细的介绍?
这里有包含源码解析的详解: Alibaba Sentinel 限流、熔断实现详解

为什么用时间窗口(Node计算规则实现)

这段主要描述的内容是Sentinel怎么精确的控制每一秒的。
此处 https://juejin.im/post/5c3607b5e51d4542253faec3 有一个非常详细的介绍, 欢迎参阅。
如下简单介绍:

滑动窗口 Sentinel 的时间统计, 全放在了滑动窗口中去执行。依次委托步骤为:

Node(StatisticNode) -> Metric(ArrayMetric) -> WindowWrap(BucketLeapArray) -> MetricBucket -> LongAdder

滑动窗口如下解释:

  • 按秒滑动: 每秒长1000ms, 分两个窗口, 则每个窗口500ms
  • 按分滑动: 每分长60000ms, 分六十个窗口, 则每个窗口1000ms

仅以每秒为例:
任意一个时间点, 一定落在一秒的前500ms, 或者落在后500ms当面毫秒 - 当前毫秒 % 500)。
滑动窗口, 通过当前时间点,一定知道自己位于哪个窗口中(LeapArray#currentWindow(long)),起始时间点是多少。
.
由上, 所有的统计(加、减)都可以基于时间窗口。

具体case:
后面计算限流的时候, 限定每秒QPS=N。 则只需要知道前500ms加上后500ms的请求数M, 加上当前这次请求需要的请求数X(每个窗口中的LongAdder, 数据, 以空间换时间, CAS累加成本太高), 是不是大于预设值N就好了。

而每次做QPS累加, 是StatisticSlot在后续Slot计算未抛出异常之后才执行的。每次获取QPS,都需要得到当前的时间窗口(需要得到锁)

那回归正题, 为什么使用时间窗口呢, 秒级的时间窗口又为什么是2个呢以我看来, 原因如下:

  1. 请求的到来并不是均匀的, 可能在一秒的前500ms根本没有请求。这个时候计算每秒的QPS需要由后500ms + 下一秒的前500ms
  2. 时间窗口的数量起码得能被时间长度(1s=1000ms/1m=60000ms)整除吧。
    • 并发环境中,时间窗口间隔越小, 当前线程位于窗口分界点的概率就越高。
    • 并发环境中,改修当前时间点所处的窗口,是需要加锁的,窗口越多,加锁越频繁。
    • 窗口多到一定程度,所有线程全花在计算时间窗口上去了。

客户端与服务端通讯

可靠性分析

本文对可靠性着手的点有可用性、稳定性、高能效。所有的CASE都基于单台机器承受QPS100上限、单次任务执行200ms计算(模拟我部门生产环境)
点会很多, 遇见一个提一个。

可靠性

高能效

  • Sentinel是有性能损失的。 甚至在高负载环境下, 可能达到5ms(数据基于生产环境混布,同机器有吃CPU的服务)

这个小结的目的是分析出在使用Sentinel上的时候遇见的坑。
实际生产活动中, 一般的企业维持100QPS的可能性并不高。还是并发环境的可能性就更低了。
Sentinel依然是一个非常优秀的, 可以服务于生产环境的预案工具。

集群限流

基于Sentinel所做易用性拓展

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值