BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

队列并不能解决“超载”

| 作者 马德奎 关注 0 他的粉丝 发布于 2014年11月26日. 估计阅读时间: 3 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

人们总是错误地使用队列,最坏的情况是用它解决“超载(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)关注我们,并与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

丢弃问题 by liu pi1ot

解决问题的办法就是丢弃问题?

不是很理解~ by 贾 珣

不是很理解~感觉比较迷茫

Re: 丢弃问题 by 预 流

丢弃或者阻塞,并且设置检查点。

不够具体 by 潘 晓雷

仅仅说了大思路,不够具体。
过载的难点是如何将全局过载和局部慢后端区分开,具体实施时还要结合运维、异常处理的方案做折中。

”惊悚“的开场白 by 李 斐然

"人们总是错误地使用队列,最坏的情况是用它解决“超载(overload)”问题“
我相信绝大多数使用队列的人都知道队列不能解决长时间的过载问题。 这个道理如此简单,所以前面四张水池的图片都是不需要的。

反压和过载看上去是有效的解决方法, 但是在本文里仅仅提到这两个名词,而没有深入的阐述。

所以, 我不知道这篇文章的意义在哪里?

我理解应该是负反馈, by 李 黄河

我理解应该是负反馈,当过载以后主动降低吞吐,增加时延,从而减少请求数据。
正常情况下不触发负馈,当负载超过阈值时负反馈机制启动,从而服务器会稳定在一个负载上。

Re: ”惊悚“的开场白 by Zhefu Zhou

正好我在生产环境中遇到类似的问题,本人开发的异步队列模块,因为某些Runnable的资源死锁问题,使得队列无限膨胀,直接导致服务器不可用。

本人的处理方式其实就是所谓的“反压”,也就是在检测到队列“满载”的时候,拒绝新任务的加入(到什么程度才叫“满载”,这是个见仁见智的问题,跟具体应用和环境有关)。但是这种拒绝或反压并不是粗暴的丢弃,事实上在企业应用中,对粗暴丢弃的行为是零容忍的。不丢弃的反压,意味着待执行的Runnable必须在一个比队列空间大得多的地方暂存起来,通常来说,它是硬盘上的DB空间。简单粗暴地序列化Runnable对象肯定不行,对一个复杂应用来说尤其不值得。在我的应用中,请求总是被首先转换为一个DTO(或XML)再进行后续的表单提交操作,由于代码逻辑总是一定的,DTO作为直接输入参数,将直接决定逻辑的执行结果。这样我的任务就简单了,只要在任务提交前序列化这个DTO将之存在DB上,就不怕队列反压丢失了。当Runnable执行完成后就去DB删除这个序列化对象(或者什么都不做),被队列拒绝后就为这一条序列化记录更新一个状态,以后在服务器不那么繁忙的时候,利用心跳机制将这些拒绝状态中的DTO取出来,重新初始化Runnable任务并再次尝试提交队列。

该反压的时候就得反压,该丢弃的时候就要丢弃,服务的质量有时表现在是否透明上。 by 赵 中

完全同意作者观点。

丢弃数据的说法很模糊 by zeng ke

在大数据时代,个人认为数据在高峰期可以支持丢弃在任何场景应该都是不允许的,数据就是互联网企业的命脉。 数据丢弃的说法较模糊,任务数据延时处理才是合理的解释。现在由很多技术都可以解决这样的问题。举个例子:MQ处理海量订单给后端业务的冲击,MQ层做海量数据缓冲,形成蓄水池,该层无状态的支持水平扩展。把订单消息存储在云存储上。而后端业务通过PULL模型主动拉数据。起到削峰的作用。拒绝数据录入方式不可取。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

9 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT