Contents

为什么微服务需要限流

首先考虑一下这样一个场景:饭点许多学生去食堂吃饭,他们在食堂需要先排队、打饭,然后寻找座位,找到座位后再吃饭。

食堂的窗口、座位数量都是有限的,也就是说食堂在一段固定时间内服务的学生数量是有限的,饭点涌入食堂的学生很可能超过这个数量。于是食堂很容易人满为患,体现为队伍越来越长,座位越来越难找。

根据我的观察,有些同学会在食堂人很多的情况下先用手机壳、耳机盒占位然后再去排队。套用一个餐饮行业常用的指标:翻桌率,这样的行为降低了座位的利用率,使得固定时间内一个座位能够服务的学生数量降低,体现就是座位越来越难找。

过去的我把这个现象称之为系统“中毒”,它体现了许多系统中悲观的情况:在高负载的情况下,随着系统负载的增加,系统的吞吐量反而下降了。

Note: 中毒只是我胡编乱造的词

翻开《计算机网络自顶向下》,其中指出路由器的四大组件:输入端口、输出端口、交换接口、路由选择处理器。其中输入端口和输出端口都可能出现排队。显然输入端口的队列长度是有限制的,输入队列已满的情况下新分组到达,路由器只能选择丢弃一些分组。 分组丢弃后往往伴随着更多分组的到来:不仅仅是 TCP 会超时重传,烦躁的用户也可能反复尝试操作,发起更多网络请求,造成网络中分组的激增,使得网络拥塞情况加剧。

如果下游路由器由于输入队列已满而丢弃分组,上游路由器转发分组的工作也会白费,使得系统中毒不断加深,体现为系统中分组丢弃引发的重传分组占比越来越高,分组丢弃更加频繁,实际转发的有效分组越来越少。

为了应对越来越多的流量,将服务中相对独立的部分拆分成一个个微服务,不同微服务通过 rpc 通信。在这样一个分布式环境下,rpc 超时约等于故障。举一个例子,淘宝的商品列表页中展示了许多商品的缩略图、名称、价格等等,当用户点开商品卡片进入商品详情页后需要展示商品大图。假设 A 服务负责商品详情页,B 服务负责商品大图(通常是商品系统或者商品中心),于是就存在 A 服务调用 B 服务获取商品大图的 rpc 调用关系。作为 C 端核心链路的一部分,A 服务的接口有很苛刻的耗时要求,假设要求 A 服务的接口耗时控制在 500ms 内,而 A 服务选择在 100ms 内完成 B 服务的 rpc 调用,若 100ms 内 B 服务没有返回,则 A 服务认为 B 服务故障,采取降级措施,例如使用商品缩略图(在商品列表页已经有了)代替商品大图,或者干脆不展示商品大图。

在上述例子中,B 服务对 A 服务来说是弱依赖。再考虑一个强依赖的情况。A 系统需要调用 B 系统,由于各种原因,这个调用可能会失败,但并非严重的错误。而调用 B 服务是 A 服务必须完成的工作,而且这个 rpc 调用不在用户触发的流程中(例如定时任务、消息队列等等),自然也无法做到返回错误让用户稍后重试。于是 A 服务很可能采取这样一个方案:调用失败后间隔一段时间重试若干次。这就好比计算机网络的重传分组。

最后再考虑这样一个情况,饭点对于外卖来说是业务高峰期,如果此时出现了服务超时,用户就会感知到加载时间变长,烦躁的用户可能反复进出页面、点击按钮,发起更多网络请求。假设每秒有 x 个用户涌入外卖平台,每个用户平均 3min 完成下单。那么可以简单的认为同时有 3 * 60 * x 个用户停留在平台中,由于服务的超时,用户平均下单耗时也显著增长,停留在平台的用户也越来越多,使得系统超时加剧,最坏的情况就是系统中充斥着烦躁的用户反复进出页面、点击按钮发起的请求,而单位时间内下单越来越少,最终体现为虽然系统负载很高,但下单量很低,服务近似不可用

为了避免微服务这样的分布式系统出现中毒,首先在设计时就要对 rpc 进行限流,不仅仅要对调用发起方限流,也要对被调用方限流。其次,不同于计算机网络中的路由器,微服务中超时的服务可以灵活的扩容,这也是出现超时后的事后应对方法。

说到限流就不得不提及 google 大名鼎鼎的令牌桶限流算法了, 但我不想重复八股,有兴趣的读者可以读一下令牌桶算法的 实现,非常简洁,不想读源码的读者也可以看看这篇 文章,我想提及两点有意思的地方

  1. 令牌桶的桶大小和路由器的输入队列长度很相似,都能控制请求积压的规模
  2. google 的平滑暖机限流器在系统负载低时允许的 QPS 较低,而随着系统负载逐渐升高,限流器允许的 QPS 也逐步升高。
    “暖机”反映了从微服务往下到硬件的整个计算机体系中缓存的大量应用(微服务本地缓存、JIT、数据库缓存、redis 缓存、操作系统缓存、磁盘缓冲区、CPU cache等等)

最初入门后端时对限流、redis 之类完全不理解为什么要这么做,毕业后进入后端行业后逐渐理解了许多,我想这就是大学学习计算机网络、操作系统的意义吧,只要理解了基本原理,那些看着更高级的概念也迎刃而解。毕竟它们背后的思想在计算机网络、操作系统这些领域早就有应用了