内容简介:[译]Discord 如何处理每分钟超过一百万次的突发请求
原文: how discord handles push request bursts of over a million per minute with elixirs genstage
译者:杰微刊兼职翻译巫明瀚
Discord团队观察到了他们的业务有巨大的增长。为了应对这种增长,我们的工程师团队开始开心的调研应该如何去扩展后端服务。
我们在Elixir's GenStage这项技术上看到了巨大的成功。
一场完美的风暴:守望先锋和Pokémon GO
今年夏天,我们的移动推送通知系统展开了了一场战斗。 / r /(注①) Overwatch的Discord刚刚超过25,000并发用户,Pokémon GO 的Discords也在这个数字左右,突发信息成为一个真正的问题。
突发信息使整个推送通知系统变得异常缓慢,并且有时还会发生停止。用户可能延迟收到或者根本收不到推送信息。
我们意识到我们可以通过XMPP而不是HTTP向Firebase发送推送请求,立即提高吞吐量。
Firebase XMPP比起HTTP要麻烦一些。 因为Firebase要求每个XMPP连接每次只能有不超过100个的待处理请求。如果你有100个请求在处理途中,那么必须等Firebase确认完该次请求之后才会发送下一个请求。
由于一次只能处理100个请求,因此我们需要设计我们的新系统,使XMPP连接在突发情况下不会超载。
从最初的考虑来看,GenStage对于我们的问题来说似乎是最佳的解决方案。
注①:reddit的/r/板块 ## GenStage的救援 经过一番调查,我们确定了将推送通知发送到Google Firebase Cloud Messaging服务的主要瓶颈。
GenStage
那么, GenStage是什么?
GenStage是一种新的Elixir行为,用来在Elixir进程之间交换事件与压力负载。
这意味这什么?基本上,它能给予你必要的工具,来确保你的系统的每一部分都获取到负载。
在实践中,具有GenStage行为的系统通常具有几个阶段(stage)。
阶段(stage)是接收并且(或者)发送来自其他阶段的数据的计算步骤。
当一个阶段发送数据时,它是生产者,当一个阶段接收数据时,它又是消费者.某个阶段可能集生产者和消费者的角色于一身。
除了生产者或消费者的角色外,如果一个阶段只生产项目,那么它被称为"源(source)",如果一个阶段只消耗项目,那么它被称为"接收器(sink)"。
工作方法
一个重要的图表
我们将系统分为了两个GenStage阶段,一个"源"和一个"接收器"
* 阶段 1 -推送收集器(Push Collector):推送收集器是收集推式请求的生产者。每台机器目前有一个推送收集器Erlang(注①)进程。
* 阶段 2 -推送器(Pusher):推送器是一个消费者,它需要来自推送收集器的推送请求,并将请求推送到Firebase,它一次只需要100个请求,以确保它不超过Firebase的待处理请求限制。每台机器有很多的推送器Erlang进程。
注①:Erlang是一种通用的并行 程序设计语言 ,它由 乔?阿姆斯特朗 (Joe Armstrong)在 瑞典 电信设备制造商 爱立信 所辖的计算机科学研究室开发。
使用GenStage进行压力负载和负载脱落
GenStage有两个关键的特性能在请求突发期间给予我们帮助:压力负载和负载脱落。
压力负载
在推送器中,我们使用GenStage的需求功能来向推送收集器发起询问,以获得推送器能处理的最大请求数量。这能确保推送器待处理请求的数量上限.当Firebase确认了这些请求后,推送器便能从推送收集器继续获得更多的请求。
推送器知道Firebase XMPP连接可以处理的请求的确切数量,并且不向推送收集器要求更多的请求。而除非推送器要求,否则推送收集器不会主动向推送器发送请求。
负载脱落
由于推送器对推送收集器施加了压力负载,因此在推送收集器处,我们会有一个潜在的隐患。特别巨大的推送突发状况可能会使推送收集器过载。
GenStage有另一个内置功能来处理这个:缓冲事件。
在推送收集器中,我们指定要缓冲的推送请求数。通常缓冲区是空的,但是在大约每月发生一次的灾难性情况下,它会派上用场。
如果系统中有太多的信息在移动,并且缓冲区被填满,则推送收集器可能将会释放传入的推送请求。这可以通过简单地在推送收集器的init函数中指定buffer_size选项来使用GenStage,而且没有额外的开销。
有了这两个功能,我们便能够处理突发通知的情况.
代码(重要部分)
下面是我们如何设置我们的阶段(stage)的示例代码。为了简单起见,我们删除了很多错误处理的代码,例如当连接断开时或者Firebase返回错误等。
如果只想查看系统的结果,可以跳过代码。
推送收集器(生产者)
推送器(消费者)
一个示例事件
下面是新系统所处理的一个真实的突发事件。上图是每秒流过系统的推送请求数。下图是由推送收集器缓冲的推送请求的数量。
注意:这些图表来自我们部署新系统后的第一个事件。自那以后,我们每秒的推送
量增加了一倍以上。此外,我们只在用户的应用不处于活动状态时发送推送。
事件发生顺序:
~17:47:00—系统一切正常.
~17:47:30—我们开始接收一串消息。推收收集器的缓冲器计数随着推送器的响应而减小。不久后,缓冲区下降了一点。
~17:48:50—推送器中信息的涌入速度要大于信息的处理速度,因此推送收集器中的缓冲区开始填充。
~17:50:00—推送器收集器缓冲器开始进入峰值并释放一些请求。
~17:50:50—推送收集器峰值停止。
~17:51:30—请求峰值开始下降.
~17:52:30—系统完全恢复正常.
在整个事件期间,对系统或用户都没有产造成明显的影响。显然这次事件中有几个通知被推送收集器释放掉了,但是如果这几个通知没有被释放,系统可能永远无法恢复,或者推送收集器可能已经崩溃。我们认为某些事情的损失是完全可以接收的折衷方案,比如这些通知。
Elixir的成功
在Discord上,我们非常高兴的使用了Elixir / Erlang作为我们的后端服务的核心技术。我们也非常兴奋能看到在Erlang/OTP的坚实的技术基础上增加了类似GenStage这样的新功能。
我们正在寻找一些具有进取精神的人来解决一些在Discird的快速增长遇到的问题。
如果你喜欢游戏并且对这些问题感到有兴趣,欢迎加入我们。
------------好久不见的分隔线------------
杰微刊旨在分享优质的内容。
我们水平有限,但理想高远。
也同样期待有理想的您对这个世界的贡献。
欢迎任何目的的联系。
欢迎关注我们
以上所述就是小编给大家介绍的《[译]Discord 如何处理每分钟超过一百万次的突发请求》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- [译]Discord 如何处理每分钟超过一百万次的突发请求
- 突发!Nginx 之父被抓
- MySQL性能突发事件问题排查技巧
- MySQL性能突发事件问题排查技巧
- [译] Grab 熔断器设计:如何应对突发打车峰值
- 复盘一次小公司遇突发流量的扑火抢救
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Android 源码设计模式解析与实战
何红辉、关爱民 / 人民邮电出版社 / 2015-11 / 79.00元
本书专门介绍Android源代码的设计模式,共26章,主要讲解面向对象的六大原则、主流的设计模式以及MVC和MVP模式。主要内容为:优化代码的首步、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则、单例模式、Builder模式、原型模式、工厂方法模式、抽象工厂模式、策略模式、状态模式、责任链模式、解释器模式、命令模式、观察者模式、备忘录模式、迭代器模式、模板方法模式、访问者模式、中介......一起来看看 《Android 源码设计模式解析与实战》 这本书的介绍吧!