队列并不能解决“超载”

  • 马德奎

2014 年 11 月 25 日

话题:语言 & 开发架构

人们总是错误地使用队列,最坏的情况是用它解决“超载(overload)”问题。Fred Hebert是《Learn You Some Erlang for Great Good!》一书的作者。在这本 Erlang 入门书籍中,他结合生动的插图、恰当的实例以浅显易懂的方式讲解了技术问题。近日,他以同样的方式阐释了为什么“队列不能解决超载”。

他将系统比作一个洗手池,如下所示:

在正常的操作下,数据只从左侧流入,出口可以处理所有数据。但在一些重大活动期间,比如圣诞节,可能会出现如下情况:

数据从左右两侧同时流入系统。如果数据输入越来越快,那么出口就可能会无法及时处理所有数据。这时,人们通常会考虑增加一个队列缓冲区(如上图的水槽)存储临时数据。但不管队列多大,持续的超载都会导致如下情况的发生:

队列满了,系统崩溃。这时候,开发人员会查看堆栈跟踪、队列、数据库查询以及调用的 API。但经过各种优化,甚至更换更大的服务器后,系统仍然无法承受这种持续的超载,因为瓶颈在出口(下图中红箭头所示的位置):

该瓶颈可能是数据库,可能是磁盘、带宽或 CPU。不消除这种瓶颈,任何优化都是徒劳。所以此时,开发人员应该做的是阻塞输入,即“反压(back-pressure)”或者丢弃数据,即“卸载(load-shedding)”。可能有人会认为,反压会招致用户的不满。但实际上,即使不主动反压,当系统负载达到一定程度后,速度也会降低,甚至崩溃。所以,虽然反压会降低用户的输入速度,但却可以保证系统的运行。另外,引入队列作为一种优化机制会违背端到端原则。因此,开发人员应该设置更多允许超时的地方,提供故障检测方法,并将其反馈给用户。

如上图所示,开发人员可以在识别出系统瓶颈后设置相应的反压机制,避免数据流入过快。而依据检查点的不同,开发人员可以对延迟和吞吐量实现不同层次的优化。

借助反压或卸载,开发人员可以获得以下好处:

  • 合适的服务质量指标
  • 减少紧急修复的次数
  • 根据账户限制和优先通道收费的方式
  • 系统更稳定

总之,如果 API 设计考虑了端到端原则和幂等性,那么反压或卸载对调用者而言通常不会成为问题,因为它们可以安全地重试请求。


感谢郭蕾对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

语言 & 开发架构