熔断,限流与隔板

公司的对象存储服务上线至今仍没有限流模块。

8月份一次 CDN 事故中所有流量直接打到源站,导致 CPU 和内存使用率突增。 为防止服务雪崩, 紧急申请了一批资源才避免引发更大的事故。

于是9月份我就有了这个限流组件的 OKR。

动手之前,发现对微服务中的 熔断, 限流, 隔板 三者的理解有一定的误区,搜索一些资料记录于此。

熔断 - Circuite Breaker

  • 问题背景

    如果被调方由于某些问题挂掉,但是并没有从服务发现节点摘除。

    主调方仍然会发起请求,虽然被调方已经无法提供服务,但由于超时时间的配置,主调不会立即失败。

    如果在流量非常大的情况下,主调方会由于请求hang住占用大量资源,进而导致主调方挂掉。

  • 解决方案

    主调挂掉的原因是等待响应的请求占用了大量的资源。而熔断器的原理就是在发生此类情况时对于调用立即返回失败。

    熔断器的一种实现方式是利用状态机,通过滑动窗口计算每个周期的状态,如下图。

    20210918163950

  • 适用场景

    • 避免因为被调服务挂掉引起自身挂掉

限流 - Throttling

  • 问题背景

    客户端请求可能由于某些问题突发爆炸性增长,服务端压力陡增。

    此时如果流量继续增长,很有可能导致服务挂掉,例如因为内存占用过大被操作系统kill等。

    为避免发生此类场景,就需要对请求限流。

  • 解决方案

    限流的常用方式如下:

    1. 拒绝服务。对于某些请求直接返回失败
    2. 服务降级。停止提供低优先级功能,降低资源占用
    3. 使用负载均衡组件平滑请求量,例如消息队列等,常见于异步请求。
  • 适用场景

    • 保护自己避免因为流量突增雪崩
    • 防止单一租户占用大量资源

隔板 - Bulkhead

  • 问题背景

    场景1: 主调方为了完成一个响应可能需要对多个背调方发起请求,假如一个被调方响应缓慢,请求堆积导致耗尽线程池,会引起大规模请求失败。

    场景2: 被调方由于某个主调出现大量请求占用大量资源,导致无法对其他主调提供服务。

    写到这里会发现,场景1和熔断的场景类似,而场景2就是限流的场景…

    基于此,隔板和熔断,限流并不是对立,而是对同一问题的不同考虑角度。

  • 解决方案

    在服务层面通常使用 隔离部署 的方式实现。

    在单个进程内部,大多使用场景是池化隔离,如下图:

    20210918182801

  • 适用场景

    • 隔离不同功能的资源消耗

    • 客户优先级隔离

    • 保护自己免于雪崩

参考链接

[1] Throttling pattern

[2] Bulkhead pattern

[3] Circuit Breaker pattern