高可用服务(一)限流降级篇

1. 服务限流

1.1 什么是服务限流

​ 针对服务最高处理能力设定阈值,对超过该阈值的请求做特殊化处理(排队、降级、拦截等),避免后端服务因突发流量或持续高位流量导致宕机。

1.2 限流的基本思想

​ 限流的本质在于流量整形(Traffic Shaping)和速率限制(Rate Limiting)。限流有两类算法实现:简单计数限流时间窗口限流

流量整形和速率限制

1.2.1 简单总量计数限流

​ 简单总量计数限流实现非常简单,常见的实现思路有:

  1. 针对整个资源调用总次数进行计数,达到限制后禁止调用

  2. 针对特定参数调用总次数进行计数(用户IP、令牌),特定参数达到限制后禁止调用简单计数限流实现简单,应用也非常广泛:连接池、线程池等,但是最大的问题在于不能限制平均速率

1.2.2 时间窗口限流

​ 为了弥补简单总量计数限流无法限制平均速率的问题引入了基于时间窗口的限流。

令牌桶算法 - Token Bucket

每次调用前去桶中取1个令牌,取到令牌才发起调用,定期向桶中添加令牌,达到限速目的

令牌桶算法

漏斗桶算法 - Leaky Bucket

将每次调用放到漏斗桶中,如果没有溢出则可以正常调用,漏斗桶流出速率固定。

漏斗桶

令牌桶和漏斗桶的区别在于:令牌桶允许突发流量,而漏斗桶则只允许恒定流量。

为了突出令牌桶允许突发流量的特性,进一步提出了租借令牌,当桶中没有令牌时允许临时获取一定量的令牌(剩余令牌数可为负),但后续调用必须等令牌补充归还后才可获取令牌。(Nginx基于此算法实现)

Linux网络限速是典型的流控算法应用场景,运用了更多高级算法CBQ流量队列控制、HTB分层令牌桶等https://www.ibm.com/developerworks/cn/linux/1412_xiehy_tc/index.html

2. 服务熔断与降级

2.1 什么是服务熔断与降级

熔断是指当某个下游服务出现异常时停止对该服务的调用。

降级则是指某些服务出现熔断或集群整体压力较大时关闭对某些服务的调用,并返回备用降级数据的操作。

二者经常同时使用,主要作用是在下游服务异常时提供有限的可用性,提升链路稳定性,避免服务雪崩

服务雪崩的过程

服务雪崩的过程

服务雪崩的过程

2.2 熔断降级的基本思想

服务熔断降级流程如下:

  1. 对服务的调用进行监控,当特定指标达到设定阈值时触发熔断降级,常见指标有:

    • 服务异常数量/比例
    • 服务响应时间
  2. 对于熔断的接口快速返回失败或返回降级数据。

  3. 定期检测熔断接口是否恢复,若恢复则取消熔断。

3. 常见限流降级框架

3.1 Hystrix vs Sentinel

Hystrix是Netflex开源的一个熔断降级框架

hystrix dashboard

Sentinel是阿里开源的限流降级框架

Sentinel

特性 Hystrix Sentinel
请求限流 X √(多种策略限流,请求数、线程数、自定义参数)
请求熔断降级
常见框架无缝集成
可视化界面
同步/异步/响应式 调用
半打开支持 √(半打开恢复检测) X
接口缓存 √(结果缓存) X
系统负载检测 X √(CPU、线程数、响应时间、总QPS)
流控模式切换 X √(直接拒绝、队列、预热)

3.2 Sentinel功能简介

  • 实时监控:受保护资源当前状态监控

  • 聚簇链路:应用资源调用路径

  • 服务限流

    流控模式:

    | 流控模式 | 简介 |
    | ——– | ———————————————————— |
    | 直接 | 直接根据 流控应用限流。流控应用除了origin名还可以填default(所有调用者)和other(需要配合origin名规则) 优先级origin > other > default |
    | 关联 | 两个资源具有竞争和依赖关系时可以使用关联来共同限制(例如数据库读写) |
    | 链路 | 多个入口对同一资源的调用可以针对某一入口进行限流 |

    1
    2
    3
    4
    5
    6
    7
    8
    链路流控
    machine-root
    / \
    / \
    Entrance1 Entrance2
    / \
    / \
    DefaultNode(nodeA) DefaultNode(nodeA)
  • 服务降级:服务异常时快速失败,避免雪崩

  • 参数限流

1
SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
  • 系统限流:针对系统全局参数进行限流
  • 授权规则:针对指定origin开放或禁止调用,default为所有应用

3.3 Sentinel原理

Sentinel的工作流程如下:

sentinel工作流程

Sentinel的核心功能由SlotChainBuilder创建的slot chain实现,slot chain使用了不完全职责涟模式,每次资源调用的元数据必须被所有槽位逐一处理,触发任意槽位的限制规则都会抛出BlockException并终止调用。

slots

3.3.1 Sentinel数据统计

服务熔断降级和限流都依赖于数据统计,Sentinel通过StatisticSlot实现数据统计。数据统计通常包括:通过QPS、拒绝QPS、线程数、成功数、异常数、资源响应时间,这些指标构成了MetricBucket类,一个时间窗口的数据通过WindowWrap存储,多个WindowWrap通过LeapArray来存储。

LeapArray

LeapArray代码片段:

LeapArrayCode

3.3.2 Sentinel规则机制

Sentinel的熔断降级和限流都是通过规则实现,规则的作用流程如下:

rule_process.png

3.3.3 Sentinel流控方式

Sentinel支持3种流控方式:快速失败、Warm Up、排队等待。 Sentinel通过Controller来实现这3中方式。

  1. 快速失败

    对于超过流控限制的请求直接抛出BlockException。快速失败对应DefaultController,该Controller使用的是令牌桶算法。

    DefaultController.png

  2. 排队等待

    使用了漏斗桶算法,控制请求匀速通过。允许的 count(每秒最大QPS / 线程数)越大,等待时间越短

    queue.gif

时间戳抢占.png

  1. Warm Up

    限制流量突增,逐步提高限制上限到配置最大值

    warmup.gif

    WarmUpController采用特殊的令牌桶实现。还没来得及细看源码实现,回头再来填坑。

3.4 Sentinel扩展

Sentinel提供了自定义扩展Slot的可能

sentinel_extend.png

在实现自己的Slot后,只需要再实现SlotChainBuilder接口替换DefaultSloatChainBuilder,并把该实现配置到SPI配置文件就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DefaultSlotChainBuilder implements SlotChainBuilder {

@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
chain.addLast(new NodeSelectorSlot());
chain.addLast(new ClusterBuilderSlot());
chain.addLast(new LogSlot());
chain.addLast(new StatisticSlot());
chain.addLast(new SystemSlot());
chain.addLast(new AuthoritySlot());
chain.addLast(new FlowSlot());
chain.addLast(new DegradeSlot());

return chain;
}

}