分布式流量治理设计

流量治理

​ 在微服务架构中,随着拆分出来的服务越来越多,随之而来会面临以下两个问题:

  • 由于某一个服务的崩溃,导致所有用到这个服务的其他服务都无法正常工作,一个点的错误经过层层传递,导致波及到调用链上的所有服务,这便是雪崩效应。
  • 服务虽然没有崩溃,但由于处理能力有限,面临超过预期的突发请求时,大部分请求直至超时都无法完成处理。

本文主要讲解服务容错和流量控制等一系列解决方案。

服务容错

容错策略

快速失败

故障转移

​ 故障转移是指如果调用的服务器出现故障,系统不会立即向调用者返回失败结果,而是自动切换到其他服务副本,尝试通过其他副本返回成功调用的结果,从而保证整体的高可用性。故障转移应该有一定的调用次数限制,比如最多允许重试三个服务,如果三个服务都调用报错,则还是会返回失败。

安全失败

​ 在一个调用链路中的服务通常也有主次之分,换句话说并不是每个服务都是必不可少的,有部分服务即使失败了也不影响核心业务的正确性,对于这类逻辑,一种理想的容错策略是即使旁路逻辑调用失败了,也当做正确的结果返回。

流量控制

流量统计指标

  • TPS 每秒事务数
  • HPS 每秒请求数
  • QPS 每秒查询数

​ 目前大多数主流系统倾向于使用HPS 作为首选的限流指标,它是相对容易观察统计的,而且能够一定程度上反应系统当前以及接下来一段时间的压力。

限流算法

限流可以帮我们应对突发流量,通常有如下几种选择:

  • 流量计数器
  • 滑动时间窗口
  • 漏桶
  • 令牌桶

流量计数器

缺点

​ 流量计数器的缺陷在于它只是针对时间点进行离散统计,为了弥补这个缺陷,一种名为滑动事件窗口的限流模式被设计出来,它可以实现平滑的基于时间片统计。

滑动窗口计数器

​ 滑动时间窗口算法在计算机科学领域中有很多成功的应用,比如编译原理中的窥孔优化、TCP协议的流量控制等都使用到滑动窗口算法。

优点

​ 滑动时间窗口算法完全弥补了流量计数器的缺陷,可以保证在任意时间片段内,只需经过简单的调用计数比较,就能控制请求次数一定不会超过限流的阀值,这在单机限流或者分布式服务单点网关中的限流中很常用。

缺点

​ 不过这种模式也有缺点,它通常只适用于否决式限流,超过阀值的流量就必须强制失败或降级,很难进行阻塞等待处理,也就很难在细粒度上对流量曲线进行整形,起不到削峰填谷的作用。

下面将介绍两种适用于阻塞式限流的限流算法。

漏桶(leaky bucket)

流量整形

​ 在计算机网络中,有个专用术语–“流量整形”,来描述如何限制网络设备的流量突变,使得网络报文以比较平均的速度向外发送。流量整形通常都使用缓冲区来实现。

漏桶算法

漏桶算法强制一个常量的输出速率而不管输入流的突发性。

规则如下:

  • 将请求放入桶中;
  • 服务定速的从桶里取出请求进行处理;
  • 桶里的请求满了就拒绝请求;
image-20210215103509486

​ 无论请求量有多大,速度有多快,经过漏桶这么一过滤,流量就平滑的流出来。但漏桶算法也存在一定的缺点,那就是无法处理突击流量,尤其是在秒杀这种场景下,突击流量会在很短的时间段内请求服务器,此时需要服务器最大限度的去处理更多的请求,但是按照漏桶算法规则执行的话,服务器依旧是按照恒定的速率处理请求,所以这是缺点所在,而令牌桶算法可以很好的解决这个问题。

实现

​ 漏桶在实现上非常简单,它其实就是一个以请求对象作为元素的先进先出的队列,队列的长度就相当于漏桶的大小,当队列已满时便拒绝新的请求进入。由于请求总是有超时时间的,所以缓冲区大小也必须是有限度的。

​ 难点在于如何确定漏桶的两个参数:桶的大小水的流出速率

​ 流出速率在漏桶算法中一般是个固定值,这对于固定拓扑结构的服务是合适的,但如果内部拓扑结构发生变化,如动态伸缩,此时流出速率可能需要动态调整。所以能够支持变动请求处理速率的令牌桶算法可能会更受程序员青睐。

令牌桶(token bucket)

​ 令牌桶与漏桶一样都是基于缓冲区的限流算法,只是方向刚好相反,漏桶是从水池里向系统发送请求,令牌桶则是系统往排队机中放入令牌。

​ 假设我们要限制系统在X秒内最大请求次数不超过Y个,那就每隔X/Y 时间往桶里放入一个令牌,当有请求进来时,首先要从桶中获取一个准入令牌,然后才能进入系统进行处理。一旦发现桶中没有令牌就应该返回失败或进行服务降级。

令牌桶算法

规则如下:

  • 定速的往桶里放入令牌;
  • 请求到达桶内先申请令牌,成功后才可以处理请求,否则拒绝处理;
  • 令牌数量达到上限后,丢弃令牌。
image-20210215103434023

基于令牌桶算法,在面对突击流量来临时,服务器可以短时间内一次性处理突击流量,用户体验更佳。

分布式限流

​ 上面讨论的限流算法和模式全部针对整个系统的限流,总是有意无意的假设系统中只有一种业务操作,并没有考虑不同业务请求进入系统的服务集群后,会分别调用哪些服务。

​ 上面讨论的限流算法直接使用在单体架构的集群上是可行的,但到了微服务架构下,它们就最多只能应用于集群最入口的网关上,对整个集群服务进行流量控制。而无法细粒度的管理流量在内部微服务节点中的流转情况。所以我们把前面介绍的限流模式都统称为单机限流,把能够细粒度控制分布式集群中每个服务流量情况的限流算法称之为分布式限流

小结

​ 对于分布式系统来说,容错是必须要有的、无法妥协的措施。但限流与容错不一样,做分布式限流一般并不追求“越彻底越好”,往往需要权衡方案付出的代价与得到的收益。

Nginx 限流

Nginx 支持的限流方式有两种:

  1. 控制并发连接数
  2. 控制速率

控制连接数

ngx_http_limit_conn_module 模块提供了限制连接数功能。

示例:

https://nginx.org/en/docs/

server {
    ...
    limit_conn perip 10;
    limit_conn perserver 100;
}

limit_conn perserver 100 作用的key是 $server_name,表示虚拟主机同时能处理并发连接的总数。

控制速率

ngx_http_limit_req_module 模块提供限制网络请求的处理速率,nginx使用漏桶算法实现限流。

示例:

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    ...
    server {
        ...
        location /search/ {
            limit_req zone=one burst=5;
        }

​ 上面的示例表示平均每秒最多允许不超过1个请求,并且突发不超过5个请求。

​ 如果请求速率超过了配置的处理速率,请求会延迟处理,以达到恒定速率处理请求。过多的请求会被延迟,直到数量达到最大突发数量 burst 为止,默认 burst 值为0,即超过速率的请求都会被拒绝。

Netflix Hystrix 限流

Sentinel 限流

Sentinel 的流量控制主要有两种统计类型:

  1. 统计线程数
  2. 统计QPS

Sentinel线程数限流不负责创建和管理线程池,而是简单统计当前请求上下文的线程个数,如果超出阈值,新的请求会被立即拒绝。

README

作者:银法王

修改记录:

​ 2023-07-01 第一次修订


分布式流量治理设计
http://jackpot-lang.online/2022/10/08/系统技术架构设计/分布式流量治理-容错与限流/
作者
Jackpot
发布于
2022年10月8日
许可协议