熔断

熔断的目的是防止应用不断尝试可能会执行失败的操作,从而使得应用可以继续执行而不用等待异常情况的修复,或者浪费时间来等待长时间的超时操作。当异常已经修复的时候,应用会再次尝试执行操作。

熔断模式类似于容易失败操作的一种代理,它能够记录和统计最近执行失败的次数,然后决定是否继续执行,或者是立即返回错误。

实现熔断模式需要考虑以下三个状态:

  1. 闭合状态:熔断器会定义一个执行操作失败的计数器,在执行操作连续失败或者高频失败时,计数器数值会自增。如果计数器值大于某个阈值,则熔断器会切换到断开状态,同时熔断器会设置一个计时器,在经过一段时间之后会切换到半开状态;
  2. 断开状态:在断开状态下,应用执行操作会立即返回失败,或者可以在当本地缓存中没有数据时返回失败;
  3. 半开状态:在半开状态下,应用会被允许执行一次或者一些操作。如果这些操作执行成功,熔断器则会切换到闭合状态,同时会重置计数器。如果这些操作继续执行失败,熔断器则会重新切换到断开状态,同时会重置计时器。

限流

限流的目的是通过对并发访问进行限速。一般来说,限流的行为如下:

  1. 把多出来的请求拒绝掉;
  2. 关闭或者降级后端服务;
  3. 把有限资源分配给重要用户;
  4. 使用队列来削去请求高峰;
  5. 通过自动化运维的方式,实现服务的自动化伸缩。

限流的经典实现方式有基于计数器、漏斗算法、令牌桶算法。

计数器

使用计数器来实现限流的算法如下:

  1. 准备执行一个请求时,计数器加一;
  2. 执行完毕一个请求时,计数器减一;
  3. 当计数器值大于某个阈值时,开始限流。

在基于计数器的限流算法中,请求是以「次数」而不是以「频率」来被限制。

漏斗算法

漏斗算法的具体步骤如下:

  1. 用一个队列(在漏斗中接水)来堆积请求;
  2. Processor(从漏斗中出水)从队列中消费请求;
  3. 如果队列满了,则开始限流。
    1. +-----------+ accept +-----------+
    2. -------->| | | | | | |-------->| Processor |
    3. +-----------+ +-----------+
    4. |
    5. | rejected
    6. v
    7. Discard

在漏斗算法中,请求是以 Processor 最大消费能力的频率来执行的。

令牌桶算法

令牌桶算法的具体步骤如下:

  1. 以恒定的速率产生 token,放入至一个集合(承担令牌桶角色)中;
  2. 准备执行一个请求时,从集合中获取一个 token;
  3. 当集合中没有 token 时,则开始限流。
    1. +-----------+ add token
    2. | | | | | | |<-----------
    3. +-----------+
    4. ^
    5. get token |
    6. | accept
    7. | +-----------+
    8. ----------+------->| Processor |
    9. | +-----------+
    10. | rejected
    11. v
    12. Discard

在令牌桶算法中,当令牌桶中有堆积的 token 时,请求可以被允许高频率地执行。

降级

降级的本质是为了解决在资源不足时,系统访问量过大的问题。当资源和访问量出现矛盾时,在资源有限的情况下,为了能够扛住大量的请求,而对系统进行降级操作。

一般来说,系统降级的策略有:

  1. 降低系统对一致性的要求;
  2. 完全停止次要功能,或者限制次要功能流量;
  3. 简化功能,例如只返回页面或者接口的部分信息。

一般来说,控制降级的形式是:

  1. 主动推送:开启系统配置;
  2. 由上游系统驱动:对外 API 的参数。