西西河

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

共:💬68 🌺272 新:
分页树展主题 · 全看
/ 5
下页 末页
  • 家园 【原创】解剖Twitter 【1】

    时常听到“浮躁”这个词,批评现代人不求甚解,缺乏严谨踏实的作风。这种批评有狭隘之嫌。每代人所处的环境不同,面临的问题不同,所以逐渐养成一种风气,去适应新的环境,解决新的问题。

    几百年前,人们读长篇小说,看歌剧,听交响乐。到了二十世纪,大家读杂志报纸,看电影电视,听流行歌曲。信息时代,人们上网,读博客,看视频。在这些表象的背后,促成这些风气进化的,是信息的产量与传播速度的激增。面对海量而且迅速更新的信息,人人捧读红楼梦,一唱三咏的局面是难以想象的。取而代之的,是要求信息的篇幅简短,而重点突出。

    随着信息爆炸的加剧,微博客网站Twitter横空出世了。用横空出世这个词来形容Twitter的成长,并不夸张。从2006年5月Twitter上线,到2007年12月,一年半的时间里,Twitter用户数从0增长到6.6万。又过了一年,2008年12月,Twitter的用户数达到5百万[1]。

    Twitter用户数的急剧攀升,与几次重大事件有关,2007年3月美国SXSW音乐节,2008年11月印度孟买的恐怖事件,2009年1月奥巴马总统就职,2009年6月伊朗选举危机等等。重大事件的报导,特点是读者多,更新快。所以,Twitter网站的成功,先决条件是能够同时给千万用户提供服务,而且提供服务的速度要快。 [2,3,4]

    有观点认为,Twitter的业务逻辑简单,所以竞争门槛低。前半句正确,但是后半句有商榷余地。Twitter的竞争力,离不开严谨的系统架构设计。

    【1】万事开头易

    Twitter的核心业务逻辑,在于Following和Be followed。[5]

    进入Twitter个人主页,你会看到你following的那些作者,最近发表的微博客。所谓微博客,就是一则短信,Twitter规定,短信的长度不得超过140个字。短信不仅可以包含普通文字信息,也可以包含URL,指向某个网页,或者照片及视频等等。这就是following的过程。

    当你写了一则短信并发表以后,你的followers会立刻在他们的个人主页中看到你写的最新短信。这就是be followed的过程。

    实现这个业务流程似乎很容易。

    1. 为每一个注册用户订制一个Be-followed的表,主要内容是每一个follower的ID。同时,也订制一个Following的表,主要内容是每一个following作者的ID。

    2. 当用户打开自己的个人空间时,Twitter先查阅Following表,找到所有following的作者的ID。然后去数据库读取每一位作者最近写的短信。汇总后按时间顺序显示在用户的个人主页上。

    3. 当用户写了一则短信时,Twitter先查阅Be-followed表,找到所有followers的IDs。然后逐个更新那些followers的主页。

    如果有follower正在阅读他的Twitter个人主页,主页里暗含的JavaScript会自动每隔几十秒,访问一下Twitter服务器,检查正在看的这个个人主页是否有更新。如果有更新,立刻下载新的主页内容。这样follower就能读到最新发表的短信了。

    从作者发表到读者获取,中间的延迟,取决于JavaScript更新的间隔,以及Twitter服务器更新每个follower的主页的时间。

    从系统架构上来说,似乎传统的三段论(Three-tier architecture [6]),足够满足这个业务逻辑的需要。事实上,最初的Twitter系统架构,的确就是三段论。

    Reference,

    [1] Fixing Twitter. (http://www.bookfm.com/courseware/coursewaredetail.html?cid=100777)

    [2] Twitter blows up at SXSW conference. (http://gawker.com/tech/next-big-thing/twitter-blows-up-at-sxsw-conference-243634.php)

    [3] First Hand Accounts of Terrorist Attacks in India on Twitter and Flickr. (http://www.techcrunch.com/2008/11/26/first-hand-accounts-of-terrorist-attacks-in-india-on-twitter/)

    [4] Social Media Takes Center Stage in Iran. (http://www.findingdulcinea.com/news/technology/2009/June/Twitter-on-Iran-a-Go-to-Source-or-Almost-Useless.html)

    [5] Twitter的这些那些. (http://www.ccthere.com/article/2363334) (http://www.ccthere.com/article/2369092)

    [6] Three tier architecture. (http://en.wikipedia.org/wiki/Multitier_architecture)

    元宝推荐:闲看蚂蚁上树,一直在看,爱莲,

    本帖一共被 1 帖 引用 (帖内工具实现)
    • 家园 【原创】【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

      【全文完】

      通宝推:高子山,
      • 家园 最近太忙

        漏了不少好贴,需要补课了。

      • 家园 不知老邓接下来准备开什么题

        可否点题,我提名Bloomberg,有人支持没。。。

      • 家园 有宝~

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

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

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

      • 家园 善有善报

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

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

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

        获益良多,谢谢

    • 家园 【原创】【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)

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

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

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

        • 家园 这个我也有疑问

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

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

          • 家园 什么顺手用什么

            可能正好他们有人实现了这么个东东,而且他们公司的文化大概是尽量自己动手,不依赖大众化的东西,就这么用上了

            • 家园 Kestrel的代码量并不大

              求人不如求己。自己开发一套,工作量不大,后期想怎么改,就怎么改,控制权在自己。

              不过,依我看,Scala不是什么好东东。既然在JVM上跑,干脆用Java得了。用Scala写,写的时候很爽快,但是后期优化就费老神了。

          • 家园 flickr 的queue也是他们自己写的

            “The primary one was that we didn’t need yet another piece of complicated architecture to maintain. The other was that for consistency and maintainability, the queue needed to run on the exact same code as the rest of the site (yes, that means our queuing system is written in php).”

            这里有一篇关于flickr queue的文章。

            http://code.flickr.com/blog/2008/09/26/flickr-engineers-do-it-offline/

            顺便说一下,我最近在做一个NetApp的项目,业余时间在看Flickr运营主管写的一本书《The Art of Capacity Planning》,这两者都与flickr有关,所以《Flickr网站架构研究》系列的下一篇只好又推迟了,抱歉。

    • 家园 【原创】【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)

分页树展主题 · 全看
/ 5
下页 末页


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

Copyright © cchere 西西河