Robinhood又双叒叕宕机了——浅谈幕后黑手“Thundering Herd”
Robinhood是美国知名的零佣金券商,尤其在年轻人中特别受欢迎。它支持零手续费交易股票、期权和加密货币交易,开创了零佣金券商的一个新时代,以致老牌劲旅TD Ameritrade、Merrill Edge等近年来也不得不割肉加入零佣金的队伍。然而三月开来,受COVID-19持续蔓延等因素的影响,美股接连几日大幅度震荡,Robinhood在三月二号和三号的交易日持续挂彩——全站宕机,导致数百万用户无法交易,推特上一片鬼哭狼嚎。
那么导致Robinhood如此大规模故障的罪魁祸首究竟是什么?因为刚过二月二十九号润日,莫非是令人啼笑皆非的润日时间bug(Leap Day Bug)么?抑或是被DDoS了?Robinhood的创始人在三号站出来发了一份公开信解释了,TL;DR就是“对不起我们错了,主要怪市场太震荡,一定吸取教训”。话音未落九号Robinhood又宕机了😂。
不过公开信透露了导致这次宕机的幕后真凶”Thundering Herd” effect ——惊群效应。且看原文:
We now understand the cause of the outage was stress on our infrastructure—which struggled with unprecedented load. That in turn led to a “thundering herd” effect—triggering a failure of our DNS system.
原来是DNS系统由于Thundering Herd打爆了,DNS一挂,也就可以理解为什么大部分外部请求都挂掉了。经典的单点故障案例,导火线是Thundering Herd。
Thundering Herd通俗的理解是一群本来被围在栅栏里吃草的牛,突然平地一声雷把它们吓出了魂,大家争着想逃出只有一扇门的栅栏,由于突然无秩序的一拥而上导致最后没几头牛逃了出去。
在通常的high scalability的web架构中,用完数据库层优化的降龙十八掌外(比如连接池,读写分离,冷热分离), 我们一般会在应用服务和数据库间加一道缓存层,以期更大程度地减轻对数据库的压力。假设80%的数据库读取都能在缓存层被缓存,正常情况下这是非常理想的情况,一来减轻了对数据库的压力,二来这些数据的访问速度也会有略微提升(大部分缓存服务都是直接读写内存而非磁盘)。但是万一缓存服务器突然宕机了呢?之前被分担的80%的数据读取压力瞬间被转移到了对高并发支持比较弱的数据库,如果应用层是在线应用,而且在时间维度上又容易呈现短时间流量剧增的情况 (比如网红在线直播),这种情况下数据库很容易出现连接超时、挂断甚至宕机。Thundering Herd背了一锅。
如果缓存服务宕机后我们立即重启是不是就能解决这个问题?假设重启需要三秒,那么终端用户最多只会在那三秒时间里感觉到问题,大部分用户一刷新页面可能就好了。事情没那么简单,上面我们提到过缓存服务大多是直接读写内存,如果缓存服务器宕机,内存里的数据就全没了,重启后的缓存服务里数据还是空的,也就是一般所说的冷启动。在冷启动阶段,数据库的压力依然很大,万一数据库在这阶段崩了,即使缓存服务恢复了也于事无补,因为缓存服务拿不到该缓存的值了。技术上我们有不少解决方案可以用来做缓存热启动或缓存预热,不过超出了本文的范围,在此略过。
还有一个很有意思的场景,在缓存服务和数据库都正常运作的情况下,突然有一百万个用户请求同时需要读同一组数据,假设这组数据恰好不在缓存里,由于是高并发的场景,我们可以肯定的是至少有一个请求从数据库拿到数据后会更新缓存,但剩下的999999个请求可能都会直接打到数据库上,数据库不幸又挂彩。Thundering Herd又背了一锅。Instagram的一篇技术博客中介绍了他们怎么解决这个问题,感兴趣的读者可以拓展阅读。简单地讲就是与其在缓存服务中存数据,不如存该数据的Promise,这样大部分请求都会等待这个Promise的回调而非抢占式读数据库。
回到Robinhood的案例,最后的瓶颈在DNS系统,但我们可以合理地猜测导火索可能是DNS系统上层的缓存服务或类似的分流服务出现故障,再加上三十年一遇的市场大幅震荡,无论是在线盯盘用户、交易用户、新注册用户都创新高,所有的用户请求同时打到应用层,应用层又直接裸奔直调DNS服务, 如果DNS系统没有做好冗余和高可用,轻轻松松地就挂彩了。
如果Robinhood之后能公开他们的Postmortem,想必会是一个很好的学习机会。但转念一想,估计盼不到那一天,Robinhood的PR团队肯定要死命拦住这种有损信誉的行为。Twitter上已经有些用户聚在一起打算告Robinhood导致他们错失了交易机会亏钱,但其实Robinhood已经在它的Service Agrement里写明了不因技术故障承担所带来的潜在用户损失💯 。所以这个故事最后告诉我们,请一个好律师写Service Agreement比请一个好程序员写代码更重要 😛
2020.3.14 于NYC