西西河

主题:【原创】解剖Twitter 【1】 -- 邓侃

共:💬68 🌺272
全看树展主题 · 分页首页 上页
/ 5
下页 末页
家园 先送花,再来挑个刺。呵呵

队列的实现方式有多种,例如Apache Mina[9]就可以用来做队列。但是Twitter团队自己动手实现了一个队列,Kestrel [10,11]。Mina与Kestrel,各自有什么优缺点,似乎还没人做过详细比较。

mina是个java 的socket开发框架,我们可以用mina来开发http server/ftp server/消息队列等各种socket server,属于底层框架;而Kestrel应该就是个消息队列,应该是上层应用。放在一起似乎不太合适阿,呵呵。

Apache MINA is a network application framework which helps users develop high performance and high scalability network applications easily. It provides an abstract · event-driven · asynchronous API over various transports such as TCP/IP and UDP/IP via Java NIO.

mina我用的比较多,Kestrel不了解,说得不一定对,呵呵,

家园 这个强顶一把。

本来我可以分析分析taobao架构的,可惜可惜,公司不让。

家园 Mina vs Kestrel

Mina的确是个底层通讯渠道,类似于C++里的ACE,不一定被限制在消息队列。你这个提醒很重要。

是不是应该把“例如Apache Mina[9]就可以用来做队列”,改成“Apache Mina是一个底层通讯渠道,稍加改造,可以用来充当消息队列”?

多谢提醒!

家园 Apache进程空循环的问题

2. apache把短信和user id转发给mongrel rails之后,处于同步等待状况。

为什么要采用这种方式?这样的话在mongrel回复之前,apache的这个连接所在的进程/线程是不能处理其他请求的,就会成为系统的瓶颈。

我的理解是,Apache空循环进程的目的是为了push。把最新更新的内容,及时push给用户。

或许有些人会问,为什么不用Java Grizzly那样的NIO的办法,这样就不需要占用一个进程,等待新更新的内容。

我的理解是,用Java Grizzly或者Erlang Yaws那样的NIO式的Web Server,的确可以节省很多资源,但是却达到同样的push的功能。

为什么Twitter不用Java Grizzly呢?看来主要还是因为Ruby on Rails。Twitter早年用了RoR,这样就绑定了Apache Web Server。想替换Apache,首先必须彻底舍弃RoR。

所以,明明知道Apache的效率不高,但是还是用它,是“历史”遗留问题,不是技术优化的考虑。

家园 如果不涉及公司内部设计

如果不涉及公司内部设计,而只是分析整理公开的资料,就可以回避这个问题了。

家园 多谢邓兄解惑,花一个。
家园 【原创】【7】 作为一种进步的不彻底

【7】 作为一种进步的不彻底

不彻底的工作方式,对于架构设计是一种进步。

当一个来自浏览器的用户请求到达Twitter后台系统的时候,第一个迎接它的,是Apache Web Server。第二个出场的,是Mongrel Rails Server。Mongrel既负责处理上传的请求,也负责处理下载的请求。Mongrel处理上传和下载的业务逻辑非常简洁,但是简洁的表象之下,却蕴含着反常规的设计。这种反常规的设计,当然不是疏忽的结果,事实上,这正是Twitter架构中,最值得注意的亮点。

点看全图

外链图片需谨慎,可能会被源头改

Figure 9. Twitter internal flows

Courtesy http://farm3.static.flickr.com/2766/4095392354_66bd4bcc30_o.png

所谓上传,是指用户写了一个新短信,上传给Twitter以便发表。而下载,是指Twitter更新读者的主页,添加最新发表的短信。Twitter下载的方式,不是读者主动发出请求的pull的方式,而是Twitter服务器主动把新内容push给读者的方式。先看上传,Mongrel处理上传的逻辑很简洁,分两步。

1. 当Mongrel收到新短信后,分配一个新的短信ID。然后把新短信的ID,连同作者ID,缓存进Vector MemCached服务器。接着,把短信ID以及正文,缓存进Row MemCached服务器。这两个缓存的内容,由Vector MemCached与Row MemCached在适当的时候,自动存放进MySQL数据库中去。

2. Mongrel在Kestrel消息队列服务器中,寻找每一个读者及作者的消息队列,如果没有,就创建新的队列。接着,Mongrel把新短信的ID,逐个放进“追”这位作者的所有在线读者的队列,以及作者本人的队列。

品味一下这两个步骤,感觉是Mongrel的工作不彻底。一,把短信及其相关IDs,缓存进Vector MemCached和Row Cached就万事大吉,而不直接负责把这些内容存入MySQL数据库。二,把短信ID扔进Kestrel消息队列,就宣告上传任务结束。Mongrel 没有用任何方式去通知作者,他的短信已经被上传。也不管读者是否能读到新发表的短信。

为什么Twitter采取了这种反常规的不彻底的工作方式?回答这个问题以前,不妨先看一看Mongrel处理下载的逻辑。把上传与下载两段逻辑联系起来,对比一下,有助于理解。Mongrel下载的逻辑也很简单,也分两步。

1. 分别从作者和读者的Kestrel消息队列中,获得新短信的ID。

2. 从Row MemCached缓存器那里获得短信正文。以及从Page MemCached那里获得读者以及作者的主页,更新这些主页,也就是添加上新的短信的正文。然后通过Apache,push给读者和作者。

对照Mongrel处理上传和下载的两段逻辑,不难发现每段逻辑都“不彻底”,合在一起才形成一个完整的流程。所谓不彻底的工作方式,反映了 Twitter架构设计的两个“分”的理念。一,把一个完整的业务流程,分割成几段相对独立的工作,每一个工作由同一台机器中不同的进程负责,甚至由不同的机器负责。二,把多个机器之间的协作,细化为数据与控制指令的传递,强调数据流与控制流的分离。

分割业务流程的做法,并不是Twitter的首创。事实上,三段论的架构,宗旨也是分割流程。Web Server负责HTTP的解析,Application Server负责业务逻辑,Database负责数据存储。遵从这一宗旨,Application Server的业务逻辑也可以进一步分割。

1996年,发明TCL语言的前伯克利大学教授John Ousterhout,在Usenix大会上做了一个主题演讲,题目是“为什么在多数情况下,多线程是一个糟糕的设计[36]”。2003年,同为伯克利大学教授的Eric Brewer及其学生们,发表了一篇题为“为什么对于高并发服务器来说,事件驱动是一个糟糕的设计[37]”。这两个伯克利大学的同事,同室操戈,他们在争论什么?

所谓多线程,简单讲就是由一根线程,从头到尾地负责一个完整的业务流程。打个比方,就像修车行的师傅每个人负责修理一辆车。而所谓事件驱动,指的是把一个完整的业务流程,分割成几个独立工作,每个工作由一个或者几个线程负责。打个比方,就像汽车制造厂里的流水线,有多个工位组成,每个工位由一位或者几位工人负责。

很显然,Twitter的做法,属于事件驱动一派。事件驱动的好处在于动态调用资源。当某一个工作的负担繁重,成为整个流程中的瓶颈的时候,事件驱动的架构可以很方便地调集更多资源,来化解压力。对于单个机器而言,多线程和事件驱动的两类设计,在性能方面的差异,并不是非常明显。但是对于分布式系统而言,事件驱动的优势发挥得更为淋漓尽致。

Twitter把业务流程做了两次分割。一,分离了Mongrel与MySQL数据库,Mongrel不直接插手MySQL数据库的操作,而是委托MemCached全权负责。二,分离了上传和下载两段逻辑,两段逻辑之间通过Kestrel队列来传递控制指令。

在John Ousterhout和Eric Brewer两位教授的争论中,并没有明确提出数据流与控制流分离的问题。所谓事件,既包括控制信号,也包括数据本身。考虑到通常数据的尺寸大,传输成本高,而控制信号的尺寸小,传输简便。把数据流与控制流分离,可以进一步提高系统效率。

在Twitter系统中,Kestrel消息队列专门用来传输控制信号,所谓控制信号,实际上就是IDs。而数据是短信正文,存放在Row MemCached中。谁去处理这则短信正文,由Kestrel去通知。

Twitter完成整个业务流程的平均时间是500ms,甚至能够提高到200-300ms,说明在Twitter分布式系统中,事件驱动的设计是成功。

Kestrel消息队列,是Twitter自行开发的。消息队列的开源实现很多,Twitter为什么不用现成的免费工具,而去费神自己研发呢?

Reference,

[36] Why threads are a bad idea (for most purposes), 1996. (http://www.stanford.edu/class/cs240/readings/threads-bad-usenix96.pdf)

[37] Why events are a bad idea (for high-concurrency servers), 2003. (http://www.cs.berkeley.edu/~brewer/papers/threads-hotos-2003.pdf)

家园 邓兄把网文当论文来写了~结构很工整~

首花~

家园 【原创】【8】 得过不且过

【8】 得过不且过

北京西直门立交桥的设计,经常遭人诟病。客观上讲,对于一座立交桥而言,能够四通八达,就算得上基本完成任务了。大家诟病的原因,主要是因为行进路线太复杂。

当然,站在设计者角度讲,他们需要综合考虑来自各方面的制约。但是考虑到世界上立交桥比比皆是,各有各的难处,然而像西直门立交桥这样让人迷惑的,还真是少见。所以,对于西直门立交桥的设计者而言,困难是客观存在的,但是改进的空间总还是有的。

点看全图

外链图片需谨慎,可能会被源头改

Figure 10. 北京西直门立交桥行进路线

Courtesy http://farm3.static.flickr.com/2671/4113112287_86cfb1cffd_o.png

大型网站的架构设计也一样,沿用传统的设计,省心又省力,但是代价是网站的性能。网站的性能不好,用户的体验也不好。Twitter这样的大型网站之所以能够一飞冲天,不仅功能的设计迎合了时代的需要,同时,技术上精益求精也是成功的必要保障。

例如,从Mongrel到MemCached之间,需要一个数据传输通道。或者严格地说,需要一个client library communicating to the memcached server。Twitter的工程师们,先用Ruby实现了一个通道。后来又用C实现了一个更快的通道。随后,不断地改进细节,不断地提升数据传输的效率。这一系列的改进,使Twitter的运行速度,从原先不设缓存时,每秒钟处理3.23个请求,到现在每秒处理139.03个请求,参见Figure 11。这个数据通道,现在定名为libmemcached,是开源项目 [38]。

点看全图

外链图片需谨慎,可能会被源头改

Figure 11. Evolving from a Ruby memcached client to a C client with optimised hashing. These changes increases Twitter performance from 3.23 requests per second without caching, to 139.03 requests per second nowadays [14].

Courtesy http://farm3.static.flickr.com/2767/4115077218_55c7250d43_o.png

又例如,Twitter系统中用消息队列来传递控制信号。这些控制信号,从插入队列,到被删除,生命周期很短。短暂的生命周期,意味着消息队列的垃圾回收 (Garbage Collection)的效率,会严重影响整个系统的效率。因此,改进垃圾回收的机制,不断提高效率,成为不可避免的问题,

Twitter使用的消息队列,原先不是Kestrel,而是用Ruby编写的一个简单的队列工具。但是如果继续沿用Ruby这种语言,性能优化的空间不大。Ruby的优点是集成了很多功能,从而大大减少了开发过程中编写程序的工作量。但是优点也同时是缺点,集成的功能太多,拖累也就多,牵一发而动全身,造成优化困难。

Twitter工程师戏言,"Ruby抗拒优化",("Ruby is optimization resistant", by Evan Weaver [14])。几经尝试以后,Twitter的工程师们最终放弃了Ruby语言,改用Scala语言,自行实现了一个队列,命名为Kestrel [39]。

改换语言的主要动机是,Scala运行在JVM之上,因此优化Garbage Collection性能的手段丰富。Figure 12. 显示了使用Kestrel以后,垃圾回收的滞后,在平时只有2ms,最高不超过4ms。高峰时段,平均滞后5ms,最高不超过35ms。

点看全图

外链图片需谨慎,可能会被源头改

Figure 12. The latency of Twitter Kestrel garbage collection [14].

Courtesy http://farm3.static.flickr.com/2617/4115072726_c611955bb2_o.png

RubyOnRails逐渐淡出Twitter,看来这是大势所趋。最后一步,也是最高潮的一步,可能是替换Mongrel。事实上,Twitter所谓“API Server”,很可能是他们替换Mongrel的前奏。

Twitter的Evan Weaver说,“API Server”的运行效率,比Apache+Mongrel组合的速度快4倍。所谓Apache+Mongrel组合,是RubyOnRails的一种实现方式。Apache+Mongrel组合,每秒能够处理139个请求,参见Figure 11,而“API Server” 每秒钟能够处理大约550个请求 [16]。换句话说,使用Apache+Mongrel组合,优点是降低了工程师们写程序的负担,但是代价是系统性能降低了4倍,用户平均等待的时间延长了4倍。

活下去通常不难,活得精彩永远很难。得过不且过,这是一种精神。

[14] Improving running component of Twitter. (http://qconlondon.com/london-2009/file?path=/qcon-london-2009/slides/EvanWeaver_ImprovingRunningComponentsAtTwitter.pdf)

[16] Updating Twitter without service disruptions. (http://gojko.net/2009/03/16/qcon-london-2009-upgrading-twitter-without-service-disruptions/)

[38] Open source project, libmemcached, by Twitter. (http://tangent.org/552/libmemcached.html)

[39] Open source project, Kestrel Messaging Queue, by Twitter. (http://github.com/robey/kestrel)

家园 【原创】【9】结语

【9】结语

这个系列讨论了Twitter架构设计,尤其是cache的应用,数据流与控制流的组织等等独特之处。把它们与抗洪抢险中,蓄洪,引流,渠道三种手段相对比,便于加深理解。同时参考实际运行的结果,验证这样的设计是否能够应付实际运行中遇到的压力。

解剖一个现实网站的架构,有一些难度。主要体现在相关资料散落各处,而且各个资料的视点不同,覆盖面也不全。更严重的问题是,这些资料不是学术论文,质量良莠不齐,而且一些文章或多或少地存在缺失,甚至错误。

单纯把这些资料罗列在一起,并不能满足全景式的解剖的需要。整理这些资料的过程,很像是侦探办案。福尔摩斯探案的方法,是证据加推理。

1. 如果观察到证据O1,而造成O1出现的原因,有可能是R1,也有可能是R2或者R3。究竟哪一个原因,才是真正的原因,需要进一步收集更多的证据,例如 O2,O3。如果造成O2 出现的可能的原因是R2和R4,造成O3 出现的可能原因是R3和R5。把所有证据O1 O2 O3,综合起来考虑,可能性最大的原因必然是(R1,R2,R3), (R2,R4), (R3,R5) 的交集,也就是R2。这是反绎推理的过程。

2. 如果反绎推理仍然不能确定什么是最可能的原因,那么假定R2是真实的原因,采用演绎推理,R2必然导致O4证据的出现。接下去要做的事情是,确认O4是否真的出现,或者寻找O4肯定不会出现的证据。以此循环。

解剖网络架构的方法,与探案很相似。只读一篇资料是不够的,需要多多收集资料,交叉印证。不仅交叉印证,而且引申印证,如果某一环节A是这样设计的,那么关联环节B必然相应地那样设计。如果一时难以确定A到底是如何设计的,不妨先确定B是如何设计的。反推回来,就知道A应该如何设计了。

解剖网站架构,不仅有益,而且有趣。

点看全图

外链图片需谨慎,可能会被源头改

Figure 13. Sherlock Holmes,福尔摩斯探案

Courtesy http://www.fantasticfiction.co.uk/images/c0/c2053.jpg

【全文完】

通宝推:高子山,
家园 善有善报

谢谢:作者意外获得【通宝】一枚

鲜花已经成功送出,可通过工具取消

提示:此次送花为此次送花为【有效送花赞扬,涨乐善、声望】

获益良多,谢谢

家园 有宝~

恭喜:你意外获得【通宝】一枚

鲜花已经成功送出,可通过工具取消

提示:此次送花为此次送花为【有效送花赞扬,涨乐善、声望】。

家园 消息队列的开源实现很多,Twitter为什么不用现成的?

好象邓兄还没有回答这个问题?

Scala运行在JVM之上,因此优化Garbage Collection性能的手段丰富
这个似乎说服力不够,基于JVM的开源消息队列应该也很多呀?

家园 这个我也有疑问

前文说过,Apache Mina是一个Java NIO工具箱,可以用来实现消息队列。

不知道有没有Mina与Kestrel的对比。不知道为什么Twitterer,放着Mina不用,而去自行开发Kestrel。

家园 猴哥吊人胃口
全看树展主题 · 分页首页 上页
/ 5
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河