解剖Twitter:Twitter系統(tǒng)架構(gòu)設(shè)計(jì)分析

0 評(píng)論 32238 瀏覽 5 收藏 61 分鐘

隨著信息爆炸的加劇,微博客網(wǎng)站Twitter橫空出世了。用橫空出世這個(gè)詞來(lái)形容Twitter的成長(zhǎng),并不夸張。從2006年5月Twitter上線(xiàn),到2007年12月,一年半的時(shí)間里,Twitter用戶(hù)數(shù)從0增長(zhǎng)到6.6萬(wàn)。又過(guò)了一年,2008年12月,Twitter的用戶(hù)數(shù)達(dá)到5百萬(wàn)。[1]

Twitter網(wǎng)站的成功,先決條件是能夠同時(shí)給千萬(wàn)用戶(hù)提供服務(wù),而且提供服務(wù)的速度要快。[2,3,4]

有觀點(diǎn)認(rèn)為,Twitter的業(yè)務(wù)邏輯簡(jiǎn)單,所以競(jìng)爭(zhēng)門(mén)檻低。前半句正確,但是后半句有商榷余地。Twitter的競(jìng)爭(zhēng)力,離不開(kāi)嚴(yán)謹(jǐn)?shù)南到y(tǒng)架構(gòu)設(shè)計(jì)。

【1】萬(wàn)事開(kāi)頭易

Twitter的核心業(yè)務(wù)邏輯,在于Following和Be followed。[5]

進(jìn)入Twitter個(gè)人主頁(yè),你會(huì)看到你following的那些作者,最近發(fā)表的微博客。所謂微博客,就是一則短信,Twitter規(guī)定,短信的長(zhǎng)度不得超過(guò)140個(gè)字。短信不僅可以包含普通文字信息,也可以包含URL,指向某個(gè)網(wǎng)頁(yè),或者照片及視頻等等。這就是following的過(guò)程。

當(dāng)你寫(xiě)了一則短信并發(fā)表以后,你的followers會(huì)立刻在他們的個(gè)人主頁(yè)中看到你寫(xiě)的最新短信。這就是be followed的過(guò)程。

實(shí)現(xiàn)這個(gè)業(yè)務(wù)流程似乎很容易。

1. 為每一個(gè)注冊(cè)用戶(hù)訂制一個(gè)Be-followed的表,主要內(nèi)容是每一個(gè)follower的ID。同時(shí),也訂制一個(gè)Following的表,主要內(nèi)容是每一個(gè)following作者的ID。

2. 當(dāng)用戶(hù)打開(kāi)自己的個(gè)人空間時(shí),Twitter先查閱Following表,找到所有following的作者的ID。然后去數(shù)據(jù)庫(kù)讀取每一位作者最近寫(xiě)的短信。匯總后按時(shí)間順序顯示在用戶(hù)的個(gè)人主頁(yè)上。

3. 當(dāng)用戶(hù)寫(xiě)了一則短信時(shí),Twitter先查閱Be-followed表,找到所有followers的IDs。然后逐個(gè)更新那些followers的主頁(yè)。

如果有follower正在閱讀他的Twitter個(gè)人主頁(yè),主頁(yè)里暗含的JavaScript會(huì)自動(dòng)每隔幾十秒,訪(fǎng)問(wèn)一下Twitter服務(wù)器,檢查正在看的這個(gè)個(gè)人主頁(yè)是否有更新。如果有更新,立刻下載新的主頁(yè)內(nèi)容。這樣follower就能讀到最新發(fā)表的短信了。

從作者發(fā)表到讀者獲取,中間的延遲,取決于JavaScript更新的間隔,以及Twitter服務(wù)器更新每個(gè)follower的主頁(yè)的時(shí)間。

從系統(tǒng)架構(gòu)上來(lái)說(shuō),似乎傳統(tǒng)的三段論(Three-tier architecture [6]),足夠滿(mǎn)足這個(gè)業(yè)務(wù)邏輯的需要。事實(shí)上,最初的Twitter系統(tǒng)架構(gòu),的確就是三段論。

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

【2】三段論

網(wǎng)站的架構(gòu)設(shè)計(jì),傳統(tǒng)的做法是三段論。所謂“傳統(tǒng)的”,并不等同于“過(guò)時(shí)的”。大型網(wǎng)站的架構(gòu)設(shè)計(jì),強(qiáng)調(diào)實(shí)用。新潮的設(shè)計(jì),固然吸引人,但是技術(shù)可能不成熟,風(fēng)險(xiǎn)高。所以,很多大型網(wǎng)站,走的是穩(wěn)妥的傳統(tǒng)的路子。

2006年5月Twitter剛上線(xiàn)的時(shí)候,為了簡(jiǎn)化網(wǎng)站的開(kāi)發(fā),他們使用了Ruby-On-Rails工具,而Ruby-On-Rails的設(shè)計(jì)思想,就是三段論。

1. 前段,即表述層(Presentation Tier) 用的工具是Apache Web Server,主要任務(wù)是解析HTTP協(xié)議,把來(lái)自不同用戶(hù)的,不同類(lèi)型的請(qǐng)求,分發(fā)給邏輯層。

2. 中段,即邏輯層 (Logic Tier)用的工具是Mongrel Rails Server,利用Rails現(xiàn)成的模塊,降低開(kāi)發(fā)的工作量。

3. 后段,即數(shù)據(jù)層 (Data Tier) 用的工具是MySQL 數(shù)據(jù)庫(kù)。

先說(shuō)后段,數(shù)據(jù)層。

Twitter 的服務(wù),可以概括為兩個(gè)核心,1. 用戶(hù),2. 短信。用戶(hù)與用戶(hù)之間的關(guān)系,是追與被追的關(guān)系,也就是Following和Be followed。對(duì)于一個(gè)用戶(hù)來(lái)說(shuō),他只讀自己“追”的那些人寫(xiě)的短信。而他自己寫(xiě)的短信,只有那些“追”自己的人才會(huì)讀。抓住這兩個(gè)核心,就不難理解 Twitter的其它功能是如何實(shí)現(xiàn)的[7]。

圍繞這兩個(gè)核心,就可以著手設(shè)計(jì)Data Schema,也就是存放在數(shù)據(jù)層(Data Tier)中的數(shù)據(jù)的組織方式。不妨設(shè)置三個(gè)表[8],

1. 用戶(hù)表:用戶(hù)ID,姓名,登錄名和密碼,狀態(tài)(在線(xiàn)與否)。

2. 短信表:短信ID,作者ID,正文(定長(zhǎng),140字),時(shí)間戳。

3. 用戶(hù)關(guān)系表,記錄追與被追的關(guān)系:用戶(hù)ID,他追的用戶(hù)IDs (Following),追他的用戶(hù)IDs (Be followed)。

再說(shuō)中段,邏輯層。

當(dāng)用戶(hù)發(fā)表一條短信的時(shí)候,執(zhí)行以下五個(gè)步驟,

1. 把該短信記錄到“短信表” 中去。

2. 從“用戶(hù)關(guān)系表”中取出追他的用戶(hù)的IDs。

3. 有些追他的用戶(hù)目前在線(xiàn),另一些可能離線(xiàn)。在線(xiàn)與否的狀態(tài),可以在“用戶(hù)表”中查到。過(guò)濾掉那些離線(xiàn)的用戶(hù)的IDs。

4. 把那些追他的并且目前在線(xiàn)的用戶(hù)的IDs,逐個(gè)推進(jìn)一個(gè)隊(duì)列(Queue)中去。

5. 從這個(gè)隊(duì)列中,逐個(gè)取出那些追他的并且目前在線(xiàn)的用戶(hù)的IDs,并且更新這些人的主頁(yè),也就是添加最新發(fā)表的這條短信。

以上這五個(gè)步驟,都由邏輯層(Logic Tier)負(fù)責(zé)。前三步容易解決,都是簡(jiǎn)單的數(shù)據(jù)庫(kù)操作。最后兩步,需要用到一個(gè)輔助工具,隊(duì)列。隊(duì)列的意義在于,分離了任務(wù)的產(chǎn)生與任務(wù)的執(zhí)行。

隊(duì)列的實(shí)現(xiàn)方式有多種,例如Apache Mina[9]就可以用來(lái)做隊(duì)列。但是Twitter團(tuán)隊(duì)自己動(dòng)手實(shí)現(xiàn)了一個(gè)隊(duì)列,Kestrel [10,11]。Mina與Kestrel,各自有什么優(yōu)缺點(diǎn),似乎還沒(méi)人做過(guò)詳細(xì)比較。

不管是Kestrel還是Mina,看起來(lái)都很復(fù)雜?;蛟S有人問(wèn),為什么不用簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)隊(duì)列,例如動(dòng)態(tài)鏈表,甚至靜態(tài)數(shù)組?如果邏輯層只在一臺(tái)服務(wù)器上運(yùn)行,那么對(duì)動(dòng)態(tài)鏈表和靜態(tài)數(shù)組這樣的簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),稍加改造,的確可以當(dāng)作隊(duì)列使用。Kestrel和Mina這些“重量級(jí)”的隊(duì)列,意義在于支持聯(lián)絡(luò)多臺(tái)機(jī)器的,分布式的隊(duì)列。在本系列以后的篇幅中,將會(huì)重點(diǎn)介紹。

最后說(shuō)說(shuō)前段,表述層。

表述層的主要職能有兩 個(gè),1. HTTP協(xié)議處理器(HTTP Processor),包括拆解接收到的用戶(hù)請(qǐng)求,以及封裝需要發(fā)出的結(jié)果。2. 分發(fā)器(Dispatcher),把接收到的用戶(hù)請(qǐng)求,分發(fā)給邏輯層的機(jī)器處理。如果邏輯層只有一臺(tái)機(jī)器,那么分發(fā)器無(wú)意義。但是如果邏輯層由多臺(tái)機(jī)器組成,什么樣的請(qǐng)求,發(fā)給邏輯層里面哪一臺(tái)機(jī)器,就大有講究了。邏輯層里眾多機(jī)器,可能各自專(zhuān)門(mén)負(fù)責(zé)特定的功能,而在同功能的機(jī)器之間,要分?jǐn)偣ぷ?,使?fù)載均衡。

訪(fǎng)問(wèn)Twitter網(wǎng)站的,不僅僅是瀏覽器,而且還有手機(jī),還有像QQ那樣的電腦桌面工具,另外還有各式各樣的網(wǎng)站插件,以便把其它網(wǎng)站聯(lián)系到Twitter.com上來(lái)[12]。因此,Twitter的訪(fǎng)問(wèn)者與Twitter網(wǎng)站之間的通訊協(xié)議,不一定是HTTP,也存在其它協(xié)議。

三段論的Twitter架構(gòu),主要是針對(duì)HTTP協(xié)議的終端。但是對(duì)于其它協(xié)議的終端,Twitter的架構(gòu)沒(méi)有明顯地劃分成三段,而是把表述層和邏輯層合二為一,在Twitter的文獻(xiàn)中,這二合一經(jīng)常被稱(chēng)為“API”。

綜上所述,一個(gè)能夠完成Twitter基本功能的,簡(jiǎn)單的架構(gòu)如Figure 1 所示。或許大家會(huì)覺(jué)得疑惑,這么出名的網(wǎng)站,架構(gòu)就這么簡(jiǎn)單?Yes and No,2006年5月Twitter剛上線(xiàn)的時(shí)候,Twitter架構(gòu)與Figure 1差距不大,不一樣的地方在于加了一些簡(jiǎn)單的緩存(Cache)。即便到了現(xiàn)在,Twitter的架構(gòu)依然可以清晰地看到Figure 1 的輪廓。

Figure 1. The essential 3-tier of Twitter architecture
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/d22c_4051785892_e677ae9d33_o.png

Reference,

[7] Tweets中常用的工具 (http://www.ccthere.com/article/2383833)
[8] 構(gòu)建基于PHP的微博客服務(wù) (http://webservices.ctocio.com.cn/188/9092188.shtml)
[9] Apache Mina Homepage (http://mina.apache.org/)
[10] Kestrel Readme (http://github.com/robey/kestrel)
[11] A Working Guide to Kestrel. (http://github.com/robey/kestrel/blob/master/docs/guide.md)
[12] Alphabetical List of Twitter Services and Applications (http://en.wikipedia.org/wiki/List_of_Twitter_services_and_applications)

【3】Cache == Cash

Cache == Cash,緩存等于現(xiàn)金收入。雖然這話(huà)有點(diǎn)夸張,但是正確使用緩存,對(duì)于大型網(wǎng)站的建設(shè),是至關(guān)重要的大事。網(wǎng)站在回應(yīng)用戶(hù)請(qǐng)求時(shí)的反應(yīng)速度,是影響用戶(hù)體驗(yàn)的一大因素。而影響速度的原因有很多,其中一個(gè)重要的原因在于硬盤(pán)的讀寫(xiě)(Disk IO)。

Table 1 比較了內(nèi)存(RAM),硬盤(pán)(Disk),以及新型的閃存(Flash),在讀寫(xiě)方面的速度比較。硬盤(pán)的讀寫(xiě),速度比內(nèi)存的慢了百萬(wàn)倍。所以,要提高網(wǎng)站的速度,一個(gè)重要措施是盡可能把數(shù)據(jù)緩存在內(nèi)存里。當(dāng)然,在硬盤(pán)里也必須保留一個(gè)拷貝,以此防范萬(wàn)一由于斷電,內(nèi)存里的數(shù)據(jù)丟失的情況發(fā)生。

Table 1. Storage media comparison of Disk, Flash and RAM [13]
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/9d42_4060534279_f575212c12_o.png

Twitter 工程師認(rèn)為,一個(gè)用戶(hù)體驗(yàn)良好的網(wǎng)站,當(dāng)一個(gè)用戶(hù)請(qǐng)求到達(dá)以后,應(yīng)該在平均500ms以?xún)?nèi)完成回應(yīng)。而Twitter的理想,是達(dá)到200ms- 300ms的反應(yīng)速度[17]。因此在網(wǎng)站架構(gòu)上,Twitter大規(guī)模地,多層次多方式地使用緩存。Twitter在緩存使用方面的實(shí)踐,以及從這些實(shí)踐中總結(jié)出來(lái)的經(jīng)驗(yàn)教訓(xùn),是Twitter網(wǎng)站架構(gòu)的一大看點(diǎn)。

Figure 2. Twitter architecture with Cache
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/0ea9_4065827637_bb2ccc8e3f_o.png

哪里需要緩存?越是Disk IO頻繁的地方,越需要緩存。

前面說(shuō)到,Twitter業(yè)務(wù)的核心有兩個(gè),用戶(hù)和短信(Tweet)。圍繞這兩個(gè)核心,數(shù)據(jù)庫(kù)中存放著若干表,其中最重要的有三個(gè),如下所示。這三個(gè)表的設(shè)置,是旁觀者的猜測(cè),不一定與Twitter的設(shè)置完全一致。但是萬(wàn)變不離其宗,相信即便有所不同,也不會(huì)本質(zhì)區(qū)別。

1. 用戶(hù)表:用戶(hù)ID,姓名,登錄名和密碼,狀態(tài)(在線(xiàn)與否)。
2. 短信表:短信ID,作者ID,正文(定長(zhǎng),140字),時(shí)間戳。
3. 用戶(hù)關(guān)系表,記錄追與被追的關(guān)系:用戶(hù)ID,他追的用戶(hù)IDs (Following),追他的用戶(hù)IDs (Be followed)。

有沒(méi)有必要把這幾個(gè)核心的數(shù)據(jù)庫(kù)表統(tǒng)統(tǒng)存放到緩存中去?Twitter的做法是把這些表拆解,把其中讀寫(xiě)最頻繁的列放進(jìn)緩存。

1. Vector Cache and Row Cache

具體來(lái)說(shuō),Twitter工程師認(rèn)為最重要的列是IDs。即新發(fā)表的短信的IDs,以及被頻繁閱讀的熱門(mén)短信的IDs,相關(guān)作者的IDs,以及訂閱這些作者的讀者的IDs。把這些IDs存放進(jìn)緩存 (Stores arrays of tweet pkeys [14])。在Twitter文獻(xiàn)中,把存放這些IDs的緩存空間,稱(chēng)為Vector Cache [14]。

Twitter工程師認(rèn)為,讀取最頻繁的內(nèi)容是這些IDs,而短信的正文在其次。所以他們決定,在優(yōu)先保證Vector Cache所需資源的前提下,其次重要的工作才是設(shè)立Row Cache,用于存放短信正文。

命中率(Hit Rate or Hit Ratio)是測(cè)量緩存效果的最重要指標(biāo)。如果一個(gè)或者多個(gè)用戶(hù)讀取100條內(nèi)容,其中99條內(nèi)容存放在緩存中,那么緩存的命中率就是99%。命中率越高,說(shuō)明緩存的貢獻(xiàn)越大。

設(shè)立Vector Cache和Row Cache后,觀測(cè)實(shí)際運(yùn)行的結(jié)果,發(fā)現(xiàn)Vector Cache的命中率是99%,而Row Cache的命中率是95%,證實(shí)了Twitter工程師早先押注的,先IDs后正文的判斷。

Vector Cache和Row Cache,使用的工具都是開(kāi)源的MemCached [15]。

2. Fragment Cache and Page Cache

前文說(shuō)到,訪(fǎng)問(wèn)Twitter網(wǎng)站的,不僅僅是瀏覽器,而且還有手機(jī),還有像QQ那樣的電腦桌面工具,另外還有各式各樣的網(wǎng)站插件,以便把其它網(wǎng)站聯(lián)系到Twitter.com上來(lái)[12]。接待這兩類(lèi)用戶(hù)的,是以Apache Web Server為門(mén)戶(hù)的Web通道,以及被稱(chēng)為“API”的通道。其中API通道受理的流量占總流量的80%-90% [16]。

所以,繼Vector Cache和Row Cache以后,Twitter工程師們把進(jìn)一步建筑緩存的工作,重點(diǎn)放在如何提高API通道的反應(yīng)速度上。

讀者頁(yè)面的主體,顯示的是一條又一條短信。不妨把整個(gè)頁(yè)面分割成若干局部,每個(gè)局部對(duì)應(yīng)一條短信。所謂Fragment,就是指頁(yè)面的局部。除短信外,其它內(nèi)容例如Twitter logo等等,也是Fragment。如果一個(gè)作者擁有眾多讀者,那么緩存這個(gè)作者寫(xiě)的短信的布局頁(yè)面(Fragment),就可以提高網(wǎng)站整體的讀取效率。這就是Fragment Cache的使命。

對(duì)于一些人氣很旺的作者,讀者們不僅會(huì)讀他寫(xiě)的短信,而且會(huì)訪(fǎng)問(wèn)他的主頁(yè),所以,也有必要緩存這些人氣作者的個(gè)人主頁(yè)。這就是Page Cache的使命。

Fragment Cache和Page Cache,使用的工具也是MemCached。

觀測(cè)實(shí)際運(yùn)行的結(jié)果,F(xiàn)ragment Cache的命中率高達(dá)95%,而Page Cache的命中率只有40%。雖然Page Cache的命中率低,但是它的內(nèi)容是整個(gè)個(gè)人主頁(yè),所以占用的空間卻不小。為了防止Page Cache爭(zhēng)奪Fragment Cache的空間,在物理部署時(shí),Twitter工程師們把Page Cache分離到不同的機(jī)器上去。

3. HTTP Accelerator

解決了API通道的緩存問(wèn)題,接下去Twitter工程師們著手處理Web通道的緩存問(wèn)題。經(jīng)過(guò)分析,他們認(rèn)為Web通道的壓力,主要來(lái)自于搜索。尤其是面臨突發(fā)事件時(shí),讀者們會(huì)搜索相關(guān)短信,而不理會(huì)這些短信的作者,是不是自己“追”的那些作者。

要降低搜索的壓力,不妨把搜索關(guān)鍵詞,及其對(duì)應(yīng)的搜索結(jié)果,緩存起來(lái)。Twitter工程師們使用的緩存工具,是開(kāi)源項(xiàng)目Varnish [18]。

比較有趣的事情是,通常把Varnish部署在Web Server之外,面向Internet的位置。這樣,當(dāng)用戶(hù)訪(fǎng)問(wèn)網(wǎng)站時(shí),實(shí)際上先訪(fǎng)問(wèn)Varnish,讀取所需內(nèi)容。只有在Varnish沒(méi)有緩存相應(yīng)內(nèi)容時(shí),用戶(hù)請(qǐng)求才被轉(zhuǎn)發(fā)到Web Server上去。而Twitter的部署,卻是把Varnish放在Apache Web Server內(nèi)側(cè)[19]。原因是Twitter的工程師們覺(jué)得Varnish的操作比較復(fù)雜,為了降低Varnish崩潰造成整個(gè)網(wǎng)站癱瘓的可能性,他們便采取了這種古怪而且保守的部署方式。

Apache Web Server的主要任務(wù),是解析HTTP,以及分發(fā)任務(wù)。不同的Mongrel Rails Server負(fù)責(zé)不同的任務(wù),但是絕大多數(shù)Mongrel Rails Server,都要與Vector Cache和Row Cache聯(lián)系,讀取數(shù)據(jù)。Rails Server如何與MemCached聯(lián)系呢?Twitter工程師們自行開(kāi)發(fā)了一個(gè)Rails插件(Gem),稱(chēng)為CacheMoney。

雖然Twitter沒(méi)有公開(kāi)Varnish的命中率是多少,但是[17]聲稱(chēng),使用了Varnish以后,導(dǎo)致整個(gè)Twitter.com網(wǎng)站的負(fù)載下降了50%,參見(jiàn)Figure 3.

Figure 3. Cache decreases Twitter.com load by 50% [17]
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/0b3a_4061273900_2d91c94374_o.png

Reference,

[12] Alphabetical List of Twitter Services and Applications.
(http://en.wikipedia.org/wiki/List_of_Twitter_services_and_applications)
[13] How flash changes the DBMS world.
(http://hansolav.net/blog/content/binary/HowFlashMemory.pdf)
[14] Improving running component of Twitter.
(http://qconlondon.com/london-2009/file?path=/qcon-london-2009/slides/
EvanWeaver_ImprovingRunningComponentsAtTwitter.pdf)
[15] A high-performance, general-purposed, distributed memory object caching system.
(http://www.danga.com/memcached/)
[16] Updating Twitter without service disruptions.
(http://gojko.net/2009/03/16/qcon-london-2009-upgrading-twitter-without-service-disruptions/)
[17] Fixing Twitter. (http://assets.en.oreilly.com/1/event/29/
Fixing_Twitter_Improving_the_Performance_and_Scalability_of_the_World_s_
Most_Popular_Micro-blogging_Site_Presentation%20Presentation.pdf)
[18] Varnish, a high-performance HTTP accelerator.
(http://varnish.projects.linpro.no/)
[19] How to use Varnish in Twitter.com?
(http://projects.linpro.no/pipermail/varnish-dev/2009-February/000968.html)
[20] CacheMoney Gem, an open-source write-through caching library.
(http://github.com/nkallen/cache-money)

【4】抗洪需要隔離

如果說(shuō)如何巧用Cache是Twitter的一大看點(diǎn),那么另一大看點(diǎn)是它的消息隊(duì)列(Message Queue)。為什么要使用消息隊(duì)列?[14]的解釋是“隔離用戶(hù)請(qǐng)求與相關(guān)操作,以便燙平流量高峰 (Move operations out of the synchronous request cycle, amortize load over time)”。

為了理解這段話(huà)的意思,不妨來(lái)看一個(gè)實(shí)例。2009年1月20日星期二,美國(guó)總統(tǒng)Barack Obama就職并發(fā)表演說(shuō)。作為美國(guó)歷史上第一位黑人總統(tǒng),Obama的就職典禮引起強(qiáng)烈反響,導(dǎo)致Twitter流量猛增,如Figure 4 所示。

Figure 4. Twitter burst during the inauguration of Barack Obama, 1/20/2009, Tuesday
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/d594_4071879010_19fb519124_o.png

其中洪峰時(shí)刻,Twitter網(wǎng)站每秒鐘收到350條新短信,這個(gè)流量洪峰維持了大約5分鐘。根據(jù)統(tǒng)計(jì),平均每個(gè)Twitter用戶(hù)被120人“追”,這就 是說(shuō),這350條短信,平均每條都要發(fā)送120次 [16]。這意味著,在這5分鐘的洪峰時(shí)刻,Twitter網(wǎng)站每秒鐘需要發(fā)送350 x 120 = 42,000條短信。

面對(duì)洪峰,如何才能保證網(wǎng)站不崩潰?辦法是迅速接納,但是推遲服務(wù)。打個(gè)比方,在晚餐高峰時(shí)段,餐館常??蜐M(mǎn)。對(duì)于新來(lái)的顧客,餐館服務(wù)員不是拒之門(mén)外,而是讓這些顧客在休息廳等待。這就是[14] 所說(shuō)的 “隔離用戶(hù)請(qǐng)求與相關(guān)操作,以便燙平流量高峰”。

如何實(shí)施隔離呢?當(dāng)一位用戶(hù)訪(fǎng)問(wèn)Twitter網(wǎng)站時(shí),接待他的是Apache Web Server。Apache做的事情非常簡(jiǎn)單,它把用戶(hù)的請(qǐng)求解析以后,轉(zhuǎn)發(fā)給Mongrel Rails Sever,由Mongrel負(fù)責(zé)實(shí)際的處理。而Apache騰出手來(lái),迎接下一位用戶(hù)。這樣就避免了在洪峰期間,用戶(hù)連接不上Twitter網(wǎng)站的尷尬局面。

雖然Apache的工作簡(jiǎn)單,但是并不意味著Apache可以接待無(wú)限多的用戶(hù)。原因是Apache解析完用戶(hù)請(qǐng)求,并且轉(zhuǎn)發(fā)給 Mongrel Server以后,負(fù)責(zé)解析這個(gè)用戶(hù)請(qǐng)求的進(jìn)程(process),并沒(méi)有立刻釋放,而是進(jìn)入空循環(huán),等待Mongrel Server返回結(jié)果。這樣,Apache能夠同時(shí)接待的用戶(hù)數(shù)量,或者更準(zhǔn)確地說(shuō),Apache能夠容納的并發(fā)的連接數(shù)量(concurrent connections),實(shí)際上受制于Apache能夠容納的進(jìn)程數(shù)量。Apache系統(tǒng)內(nèi)部的進(jìn)程機(jī)制參見(jiàn)Figure 5,其中每個(gè)Worker代表一個(gè)進(jìn)程。

Apache能夠容納多少個(gè)并發(fā)連接呢?[22]的實(shí)驗(yàn)結(jié)果是4,000個(gè),參見(jiàn)Figure 6。如何才能提高Apache的并發(fā)用戶(hù)容量呢?一種思路是不讓連接受制于進(jìn)程。不妨把連接作為一個(gè)數(shù)據(jù)結(jié)構(gòu),存放到內(nèi)存中去,釋放進(jìn)程,直到 Mongrel Server返回結(jié)果時(shí),再把這個(gè)數(shù)據(jù)結(jié)構(gòu)重新加載到進(jìn)程上去。

事實(shí)上Yaws Web Server[24],就是這么做的[23]。所以,Yaws能夠容納80,000以上的并發(fā)連接,這并不奇怪。但是為什么Twitter用 Apache,而不用Yaws呢?或許是因?yàn)閅aws是用Erlang語(yǔ)言寫(xiě)的,而Twitter工程師對(duì)這門(mén)新語(yǔ)言不熟悉 (But you need in house Erlang experience [17])。

Figure 5. Apache web server system architecture [21]
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/3b81_4071355801_db6c8cd6c0_o.png

Figure 6. Apache vs. Yaws.
The horizonal axis shows the parallel requests,
the vertical one shows the throughput (KBytes/second).
The red curve is Yaws, running on NFS.
The blue one is Apache, running on NFS,
while the green one is also Apache but on a local file system.
Apache dies at about 4,000 parallel sessions,
while Yaws is still functioning at over 80,000 parallel connections. [22]
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/8fa1_4072077210_3c3a507a8a_o.jpg

Reference,

[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/)
[17] Fixing Twitter. (http://assets.en.oreilly.com/1/event/29/Fixing_Twitter_
Improving_the_Performance_and_Scalability_of_the_World_s_Most_Popular_
Micro-blogging_Site_Presentation%20Presentation.pdf)
[21] Apache system architecture.
(http://www.fmc-modeling.org/download/publications/
groene_et_al_2002-architecture_recovery_of_apache.pdf)
[22] Apache vs Yaws. (http://www.sics.se/~joe/apachevsyaws.html)
[23] 質(zhì)疑Apache和Yaws的性能比較. (http://www.javaeye.com/topic/107476)
[24] Yaws Web Server. (http://yaws.hyber.org/)
[25] Erlang Programming Language. (http://www.erlang.org/)

【5】數(shù)據(jù)流與控制流

通過(guò)讓Apache進(jìn)程空循環(huán)的辦法,迅速接納用戶(hù)的訪(fǎng)問(wèn),推遲服務(wù),說(shuō)白了是個(gè)緩兵之計(jì),目的是讓用戶(hù)不至于收到“HTTP 503” 錯(cuò)誤提示,“503錯(cuò)誤” 是指 “服務(wù)不可用(Service Unavailable)”,也就是網(wǎng)站拒絕訪(fǎng)問(wèn)。

大禹治水,重在疏導(dǎo)。真正的抗洪能力,體現(xiàn)在蓄洪和泄洪兩個(gè)方面。蓄洪容易理解,就是建水庫(kù),要么建一個(gè)超大的水庫(kù),要么造眾多小水庫(kù)。泄洪包括兩個(gè)方面,1. 引流,2. 渠道。

對(duì)于Twitter系統(tǒng)來(lái)說(shuō),龐大的服務(wù)器集群,尤其是以MemCached為主的眾多的緩存,體現(xiàn)了蓄洪的容量。引流的手段是Kestrel消息隊(duì)列,用于傳遞控制指令。渠道是機(jī)器與機(jī)器之間的數(shù)據(jù)傳輸通道,尤其是通往MemCached的數(shù)據(jù)通道。渠道的優(yōu)劣,在于是否通暢。

Twitter的設(shè)計(jì),與大禹的做法,形相遠(yuǎn),實(shí)相近。Twitter系統(tǒng)的抗洪措施,體現(xiàn)在有效地控制數(shù)據(jù)流,保證在洪峰到達(dá)時(shí),能夠及時(shí)把數(shù)據(jù)疏散到多個(gè)機(jī)器上去,從而避免壓力過(guò)度集中,造成整個(gè)系統(tǒng)的癱瘓。

2009 年6月,Purewire公司通過(guò)爬Twitter網(wǎng)站,跟蹤Twitter用戶(hù)之間“追”與“被追”的關(guān)系,估算出Twitter用戶(hù)總量在 7,000,000左右 [26]。在這7百萬(wàn)用戶(hù)中,不包括那些既不追別人,也不被別人追的孤立用戶(hù)。也不包括孤島人群,孤島內(nèi)的用戶(hù)只相互追與被追,不與外界聯(lián)系。如果加上這 些孤立用戶(hù)和孤島用戶(hù)群,目前Twitter的用戶(hù)總數(shù),或許不會(huì)超過(guò)1千萬(wàn)。

截止2009年3月,中國(guó)移動(dòng)用戶(hù)數(shù)已達(dá) 4.7億戶(hù)[27]。如果中國(guó)移動(dòng)的飛信[28] 和139說(shuō)客[29] 也想往Twitter方向發(fā)展,那么飛信和139的抗洪能力應(yīng)該設(shè)計(jì)到多少呢?簡(jiǎn)單講,需要把Twitter系統(tǒng)的現(xiàn)有規(guī)模,至少放大47倍。所以,有人 這樣評(píng)論移動(dòng)互聯(lián)網(wǎng)產(chǎn)業(yè),“在中國(guó)能做到的事情,在美國(guó)一定能做到。反之,不成立”。

但是無(wú)論如何,他山之石可以攻玉。這就是我們研究Twitter的系統(tǒng)架構(gòu),尤其是它的抗洪機(jī)制的目的。

Figure 7. Twitter internal flows
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/fe8f_4095392354_66bd4bcc30_o.png
下面舉個(gè)簡(jiǎn)單的例子,剖析一下Twitter網(wǎng)站內(nèi)部的流程,借此考察Twitter系統(tǒng)有哪些機(jī)制,去實(shí)現(xiàn)抗洪的三要素,“水庫(kù)”,“引流”和“渠道”。
假設(shè)有兩個(gè)作者,通過(guò)瀏覽器,在Twitter網(wǎng)站上發(fā)表短信。有一個(gè)讀者,也通過(guò)瀏覽器,訪(fǎng)問(wèn)網(wǎng)站并閱讀他們寫(xiě)的短信。

1. 作者的瀏覽器與網(wǎng)站建立連接,Apache Web Server分配一個(gè)進(jìn)程(Worker Process)。作者登錄,Twitter查找作者的ID,并作為Cookie,記憶在HTTP郵包的頭屬性里。

2. 瀏覽器上傳作者新寫(xiě)的短信(Tweet),Apache收到短信后,把短信連同作者ID,轉(zhuǎn)發(fā)給Mongrel Rails Server。然后Apache進(jìn)程進(jìn)入空循環(huán),等待Mongrel的回復(fù),以便更新作者主頁(yè),把新寫(xiě)的短信添加上去。

3. Mongrel收到短信后,給短信分配一個(gè)ID,然后把短信ID與作者ID,緩存到Vector MemCached服務(wù)器上去。

同時(shí),Mongrel讓Vector MemCached查找,有哪些讀者“追”這位作者。如果Vector MemCached沒(méi)有緩存這些信息,Vector MemCached自動(dòng)去MySQL數(shù)據(jù)庫(kù)查找,得到結(jié)果后,緩存起來(lái),以備日后所需。然后,把讀者IDs回復(fù)給Mongrel。

接著,Mongrel把短信ID與短信正文,緩存到Row MemCached服務(wù)器上去。

4. Mongrel通知Kestrel消息隊(duì)列服務(wù)器,為每個(gè)作者及讀者開(kāi)設(shè)一個(gè)隊(duì)列,隊(duì)列的名稱(chēng)中隱含用戶(hù)ID。如果Kestrel服務(wù)器中已經(jīng)存在這些隊(duì)列,那就延用以往的隊(duì)列。

對(duì)應(yīng)于每個(gè)短信,Mongrel已經(jīng)從Vector MemCached那里知道,有哪些讀者追這條短信的作者。Mongrel把這條短信的ID,逐個(gè)放進(jìn)每位讀者的隊(duì)列,以及作者本人的隊(duì)列。

5. 同一臺(tái)Mongrel Server,或者另一臺(tái)Mongrel Server,在處理某個(gè)Kestrel隊(duì)列中的消息前,從這個(gè)隊(duì)列的名稱(chēng)中解析出相應(yīng)的用戶(hù)ID,這個(gè)用戶(hù),既可能是讀者,也可能是作者。

然后Mongrel從Kestrel隊(duì)列中,逐個(gè)提取消息,解析消息中包含的短信ID。并從Row MemCached緩存器中,查找對(duì)應(yīng)于這個(gè)短信ID的短信正文。

這時(shí),Mongrel既得到了用戶(hù)的ID,也得到了短信正文。接下去Mongrel就著手更新用戶(hù)的主頁(yè),添加上這條短信的正文。

6. Mongrel把更新后的作者的主頁(yè),傳遞給正在空循環(huán)的Apache的進(jìn)程。該進(jìn)程把作者主頁(yè)主動(dòng)傳送(push)給作者的瀏覽器。

如果讀者的瀏覽器事先已經(jīng)登錄Twitter網(wǎng)站,建立連接,那么Apache給該讀者也分配了一個(gè)進(jìn)程,該進(jìn)程也處于空循環(huán)狀態(tài)。Mongrel把更新后的讀者的主頁(yè),傳遞給相應(yīng)進(jìn)程,該進(jìn)程把讀者主頁(yè)主動(dòng)傳遞給讀者的瀏覽器。

咋一看,流程似乎不復(fù)雜。“水庫(kù)”,“引流”和“渠道”,這抗洪三要素體現(xiàn)在哪里呢?盛名之下的Twitter,妙處何在?值得細(xì)究的看點(diǎn)很多。

Reference,

[26] Twitter user statistics by Purewire, June 2009.
(http://www.nickburcher.com/2009/06/twitter-user-statistics-purewire-report.html)
[27] 截止2009年3月,中國(guó)移動(dòng)用戶(hù)數(shù)已達(dá)4.7億戶(hù).
(http://it.sohu.com/20090326/n263018002.shtml)
[28] 中國(guó)移動(dòng)飛信網(wǎng). (http://www.fetion.com.cn/)
[29] 中國(guó)移動(dòng)139說(shuō)客網(wǎng). (http://www.139.com/)

【6】流量洪峰與云計(jì)算

上一篇?dú)v數(shù)了一則短信從發(fā)表到被閱讀,Twitter業(yè)務(wù)邏輯所經(jīng)歷的6個(gè)步驟。表面上看似乎很乏味,但是細(xì)細(xì)咀嚼,把每個(gè)步驟展開(kāi)來(lái)說(shuō),都有一段故事。

美國(guó)年度橄欖球決賽,綽號(hào)超級(jí)碗(Super Bowl)。Super Bowl在美國(guó)的收視率,相當(dāng)于中國(guó)的央視春節(jié)晚會(huì)。2008年2月3日,星期天,該年度Super Bowl如期舉行。紐約巨人隊(duì)(Giants),對(duì)陣波士頓愛(ài)國(guó)者隊(duì)(Patriots)。這是兩支實(shí)力相當(dāng)?shù)那蜿?duì),決賽結(jié)果難以預(yù)料。比賽吸引了近一億美國(guó)人觀看電視實(shí)況轉(zhuǎn)播。

對(duì)于Twitter來(lái)說(shuō),可以預(yù)料的是,比賽進(jìn)行過(guò)程中,Twitter流量必然大漲。比賽越激烈,流量越高漲。Twitter無(wú)法預(yù)料的是,流量究竟會(huì)漲到多少,尤其是洪峰時(shí)段,流量會(huì)達(dá)到多少。

根據(jù)[31]的統(tǒng)計(jì),在Super Bowl比賽進(jìn)行中,每分鐘的流量與當(dāng)日平均流量相比,平均高出40%。在比賽最激烈時(shí),更高達(dá)150%以上。與一周前,2008年1月27日,一個(gè)平靜的星期天的同一時(shí)段相比,流量的波動(dòng)從平均10%,上漲到40%,最高波動(dòng)從35%,上漲到150%以上。

Figure 8. Twitter traffic during Super Bowl, Sunday, Feb 3, 2008 [31]. The blue line represents the percentage of updates per minute during the Super Bowl normalized to the average number of updates per minute during the rest of the day, with spikes annotated to show what people were twittering about. The green line represents the traffic of a “regular” Sunday, Jan 27, 2008.
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/d4fa_4085122087_970072e518_o.png

由此可見(jiàn),Twitter流量的波動(dòng)十分可觀。對(duì)于Twitter公司來(lái)說(shuō),如果預(yù)先購(gòu)置足夠的設(shè)備,以承受流量的變化,尤其是重大事件導(dǎo)致的洪峰流量,那么這些設(shè)備在大部分時(shí)間處于閑置狀態(tài),非常不經(jīng)濟(jì)。但是如果缺乏足夠的設(shè)備,那么面對(duì)重大事件,Twitter系統(tǒng)有可能崩潰,造成的后果是用戶(hù)流失。

怎么辦?辦法是變買(mǎi)為租。Twitter公司自己購(gòu)置的設(shè)備,其規(guī)模以應(yīng)付無(wú)重大事件時(shí)的流量壓力為限。同時(shí)租賃云計(jì)算平臺(tái)公司的設(shè)備,以應(yīng)付重大事件來(lái)臨時(shí)的洪峰流量。租賃云計(jì)算的好處是,計(jì)算資源實(shí)時(shí)分配,需求高的時(shí)候,自動(dòng)分配更多計(jì)算資源。

Twitter公司在2008年以前,一直租賃Joyent公司的云計(jì)算平臺(tái)。在2008年2月3日的Super Bowl即將來(lái)臨之際,Joyent答應(yīng)Twitter,在比賽期間免費(fèi)提供額外的計(jì)算資源,以應(yīng)付洪峰流量[32]。但是詭異的是,離大賽只剩下不到4天,Twitter公司突然于1月30日晚10時(shí),停止使用Joyent的云計(jì)算平臺(tái),轉(zhuǎn)而投奔Netcraft [33,34]。

Twitter棄Joyent,投Netcraft,其背后的原因是商務(wù)糾葛,還是擔(dān)心Joyent的服務(wù)不可靠,至今仍然是個(gè)謎。

變買(mǎi)為租,應(yīng)對(duì)洪峰,這是一個(gè)不錯(cuò)的思路。但是租來(lái)的計(jì)算資源怎么用,又是一個(gè)大問(wèn)題。查看一下[35],不難發(fā)現(xiàn)Twitter把租賃來(lái)的計(jì)算資源,大部分用于增加Apache Web Server,而Apache是Twitter整個(gè)系統(tǒng)的最前沿的環(huán)節(jié)。

為什么Twitter很少把租賃來(lái)的計(jì)算資源,分配給Mongrel Rails Server,MemCached Servers,Varnish HTTP Accelerators等等其它環(huán)節(jié)?在回答這個(gè)問(wèn)題以前,我們先復(fù)習(xí)一下前一章“數(shù)據(jù)流與控制流”的末尾,Twitter從寫(xiě)到讀的6個(gè)步驟。

這6個(gè)步驟的前2步說(shuō)到,每個(gè)訪(fǎng)問(wèn)Twitter網(wǎng)站的瀏覽器,都與網(wǎng)站保持長(zhǎng)連接。目的是一旦有人發(fā)表新的短信,Twitter網(wǎng)站在500ms以?xún)?nèi),把新短信push給他的讀者。問(wèn)題是在沒(méi)有更新的時(shí)候,每個(gè)長(zhǎng)連接占用一個(gè)Apache的進(jìn)程,而這個(gè)進(jìn)程處于空循環(huán)。所以,絕大多數(shù)Apache進(jìn)程,在絕大多數(shù)時(shí)間里,處于空循環(huán),因此占用了大量資源。

事實(shí)上,通過(guò)Apache Web Servers的流量,雖然只占Twitter總流量的10%-20%,但是Apache卻占用了Twitter整個(gè)服務(wù)器集群的50%的資源[16]。所以,從旁觀者角度來(lái)看,Twitter將來(lái)勢(shì)必罷黜Apache。但是目前,當(dāng)Twitter分配計(jì)算資源時(shí),迫不得已,只能優(yōu)先保證Apache的需求。

迫不得已只是一方面的原因,另一方面,也表明Twitter的工程師們,對(duì)其系統(tǒng)中的其它環(huán)節(jié),太有信心了。

在第四章“抗洪需要隔離”中,我們?cè)?jīng)打過(guò)一個(gè)比方,“在晚餐高峰時(shí)段,餐館常??蜐M(mǎn)。對(duì)于新來(lái)的顧客,餐館服務(wù)員不是拒之門(mén)外,而是讓這些顧客在休息廳等待”。對(duì)于Twitter系統(tǒng)來(lái)說(shuō),Apache充當(dāng)?shù)慕巧褪切菹d。只要休息廳足夠大,就能暫時(shí)穩(wěn)住用戶(hù),換句行話(huà)講,就是不讓用戶(hù)收到HTTP-503的錯(cuò)誤提示。

穩(wěn)住用戶(hù)以后,接下去的工作是高效率地提供服務(wù)。高效率的服務(wù),體現(xiàn)在Twitter業(yè)務(wù)流程6個(gè)步驟中的后4步。為什么Twitter對(duì)這4步這么有信心?

Reference,

[16] Updating Twitter without service disruptions.
(http://gojko.net/2009/03/16/qcon-london-2009-upgrading-twitter-without-service-disruptions/)
[30] Giants and Patriots draws 97.5 million US audience to the Super Bowl. (http://www.reuters.com/article/topNews/idUSN0420266320080204)
[31] Twitter traffic during Super Bowl 2008.
(http://blog.twitter.com/2008/02/highlights-from-superbowl-sunday.html)
[32] Joyent provides Twitter free extra capacity during the Super Bowl 2008.
(http://blog.twitter.com/2008/01/happy-happy-joyent.html)
[33] Twitter stopped using Joyent’s cloud at 10PM, Jan 30, 2008. (http://www.joyent.com/joyeurblog/2008/01/31/twitter-and-joyent-update/)
[34] The hasty divorce for Twitter and Joyent.
(http://www.datacenterknowledge.com/archives/2008/01/31/hasty-divorce-for-twitter-joyent/)
[35] The usage of Netcraft by Twitter.
(http://toolbar.netcraft.com/site_report?url=http://twitter.com)

【7】作為一種進(jìn)步的不徹底

不徹底的工作方式,對(duì)于架構(gòu)設(shè)計(jì)是一種進(jìn)步。

當(dāng)一個(gè)來(lái)自瀏覽器的用戶(hù)請(qǐng)求到達(dá)Twitter后臺(tái)系統(tǒng)的時(shí)候,第一個(gè)迎接它的,是Apache Web Server。第二個(gè)出場(chǎng)的,是Mongrel Rails Server。Mongrel既負(fù)責(zé)處理上傳的請(qǐng)求,也負(fù)責(zé)處理下載的請(qǐng)求。Mongrel處理上傳和下載的業(yè)務(wù)邏輯非常簡(jiǎn)潔,但是簡(jiǎn)潔的表象之下,卻蘊(yùn)含著反常規(guī)的設(shè)計(jì)。這種反常規(guī)的設(shè)計(jì),當(dāng)然不是疏忽的結(jié)果,事實(shí)上,這正是Twitter架構(gòu)中,最值得注意的亮點(diǎn)。

Figure 9. Twitter internal flows
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/fe8f_4095392354_66bd4bcc30_o.png
所謂上傳,是指用戶(hù)寫(xiě)了一個(gè)新短信,上傳給Twitter以便發(fā)表。而下載,是指Twitter更新讀者的主頁(yè),添加最新發(fā)表的短信。Twitter下載的方式,不是讀者主動(dòng)發(fā)出請(qǐng)求的pull的方式,而是Twitter服務(wù)器主動(dòng)把新內(nèi)容push給讀者的方式。先看上傳,Mongrel處理上傳的邏輯很簡(jiǎn)潔,分兩步。

1. 當(dāng)Mongrel收到新短信后,分配一個(gè)新的短信ID。然后把新短信的ID,連同作者ID,緩存進(jìn)Vector MemCached服務(wù)器。接著,把短信ID以及正文,緩存進(jìn)Row MemCached服務(wù)器。這兩個(gè)緩存的內(nèi)容,由Vector MemCached與Row MemCached在適當(dāng)?shù)臅r(shí)候,自動(dòng)存放進(jìn)MySQL數(shù)據(jù)庫(kù)中去。

2. Mongrel在Kestrel消息隊(duì)列服務(wù)器中,尋找每一個(gè)讀者及作者的消息隊(duì)列,如果沒(méi)有,就創(chuàng)建新的隊(duì)列。接著,Mongrel把新短信的ID,逐個(gè)放進(jìn)“追”這位作者的所有在線(xiàn)讀者的隊(duì)列,以及作者本人的隊(duì)列。

品味一下這兩個(gè)步驟,感覺(jué)是Mongrel的工作不徹底。一,把短信及其相關(guān)IDs,緩存進(jìn)Vector MemCached和Row Cached就萬(wàn)事大吉,而不直接負(fù)責(zé)把這些內(nèi)容存入MySQL數(shù)據(jù)庫(kù)。二,把短信ID扔進(jìn)Kestrel消息隊(duì)列,就宣告上傳任務(wù)結(jié)束。Mongrel 沒(méi)有用任何方式去通知作者,他的短信已經(jīng)被上傳。也不管讀者是否能讀到新發(fā)表的短信。

為什么Twitter采取了這種反常規(guī)的不徹底的工作方式?回答這個(gè)問(wèn)題以前,不妨先看一看Mongrel處理下載的邏輯。把上傳與下載兩段邏輯聯(lián)系起來(lái),對(duì)比一下,有助于理解。Mongrel下載的邏輯也很簡(jiǎn)單,也分兩步。

1. 分別從作者和讀者的Kestrel消息隊(duì)列中,獲得新短信的ID。

2. 從Row MemCached緩存器那里獲得短信正文。以及從Page MemCached那里獲得讀者以及作者的主頁(yè),更新這些主頁(yè),也就是添加上新的短信的正文。然后通過(guò)Apache,push給讀者和作者。

對(duì)照Mongrel處理上傳和下載的兩段邏輯,不難發(fā)現(xiàn)每段邏輯都“不徹底”,合在一起才形成一個(gè)完整的流程。所謂不徹底的工作方式,反映了 Twitter架構(gòu)設(shè)計(jì)的兩個(gè)“分”的理念。一,把一個(gè)完整的業(yè)務(wù)流程,分割成幾段相對(duì)獨(dú)立的工作,每一個(gè)工作由同一臺(tái)機(jī)器中不同的進(jìn)程負(fù)責(zé),甚至由不同的機(jī)器負(fù)責(zé)。二,把多個(gè)機(jī)器之間的協(xié)作,細(xì)化為數(shù)據(jù)與控制指令的傳遞,強(qiáng)調(diào)數(shù)據(jù)流與控制流的分離。

分割業(yè)務(wù)流程的做法,并不是Twitter的首創(chuàng)。事實(shí)上,三段論的架構(gòu),宗旨也是分割流程。Web Server負(fù)責(zé)HTTP的解析,Application Server負(fù)責(zé)業(yè)務(wù)邏輯,Database負(fù)責(zé)數(shù)據(jù)存儲(chǔ)。遵從這一宗旨,Application Server的業(yè)務(wù)邏輯也可以進(jìn)一步分割。

1996年,發(fā)明TCL語(yǔ)言的前伯克利大學(xué)教授John Ousterhout,在Usenix大會(huì)上做了一個(gè)主題演講,題目是“為什么在多數(shù)情況下,多線(xiàn)程是一個(gè)糟糕的設(shè)計(jì)[36]”。2003年,同為伯克利大學(xué)教授的Eric Brewer及其學(xué)生們,發(fā)表了一篇題為“為什么對(duì)于高并發(fā)服務(wù)器來(lái)說(shuō),事件驅(qū)動(dòng)是一個(gè)糟糕的設(shè)計(jì)[37]”。這兩個(gè)伯克利大學(xué)的同事,同室操戈,他們?cè)跔?zhēng)論什么?

所謂多線(xiàn)程,簡(jiǎn)單講就是由一根線(xiàn)程,從頭到尾地負(fù)責(zé)一個(gè)完整的業(yè)務(wù)流程。打個(gè)比方,就像修車(chē)行的師傅每個(gè)人負(fù)責(zé)修理一輛車(chē)。而所謂事件驅(qū)動(dòng),指的是把一個(gè)完整的業(yè)務(wù)流程,分割成幾個(gè)獨(dú)立工作,每個(gè)工作由一個(gè)或者幾個(gè)線(xiàn)程負(fù)責(zé)。打個(gè)比方,就像汽車(chē)制造廠里的流水線(xiàn),有多個(gè)工位組成,每個(gè)工位由一位或者幾位工人負(fù)責(zé)。

很顯然,Twitter的做法,屬于事件驅(qū)動(dòng)一派。事件驅(qū)動(dòng)的好處在于動(dòng)態(tài)調(diào)用資源。當(dāng)某一個(gè)工作的負(fù)擔(dān)繁重,成為整個(gè)流程中的瓶頸的時(shí)候,事件驅(qū)動(dòng)的架構(gòu)可以很方便地調(diào)集更多資源,來(lái)化解壓力。對(duì)于單個(gè)機(jī)器而言,多線(xiàn)程和事件驅(qū)動(dòng)的兩類(lèi)設(shè)計(jì),在性能方面的差異,并不是非常明顯。但是對(duì)于分布式系統(tǒng)而言,事件驅(qū)動(dòng)的優(yōu)勢(shì)發(fā)揮得更為淋漓盡致。

Twitter把業(yè)務(wù)流程做了兩次分割。一,分離了Mongrel與MySQL數(shù)據(jù)庫(kù),Mongrel不直接插手MySQL數(shù)據(jù)庫(kù)的操作,而是委托MemCached全權(quán)負(fù)責(zé)。二,分離了上傳和下載兩段邏輯,兩段邏輯之間通過(guò)Kestrel隊(duì)列來(lái)傳遞控制指令。

在John Ousterhout和Eric Brewer兩位教授的爭(zhēng)論中,并沒(méi)有明確提出數(shù)據(jù)流與控制流分離的問(wèn)題。所謂事件,既包括控制信號(hào),也包括數(shù)據(jù)本身??紤]到通常數(shù)據(jù)的尺寸大,傳輸成本高,而控制信號(hào)的尺寸小,傳輸簡(jiǎn)便。把數(shù)據(jù)流與控制流分離,可以進(jìn)一步提高系統(tǒng)效率。

在Twitter系統(tǒng)中,Kestrel消息隊(duì)列專(zhuān)門(mén)用來(lái)傳輸控制信號(hào),所謂控制信號(hào),實(shí)際上就是IDs。而數(shù)據(jù)是短信正文,存放在Row MemCached中。誰(shuí)去處理這則短信正文,由Kestrel去通知。

Twitter完成整個(gè)業(yè)務(wù)流程的平均時(shí)間是500ms,甚至能夠提高到200-300ms,說(shuō)明在Twitter分布式系統(tǒng)中,事件驅(qū)動(dòng)的設(shè)計(jì)是成功。

Kestrel消息隊(duì)列,是Twitter自行開(kāi)發(fā)的。消息隊(duì)列的開(kāi)源實(shí)現(xiàn)很多,Twitter為什么不用現(xiàn)成的免費(fèi)工具,而去費(fèi)神自己研發(fā)呢?

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】 得過(guò)不且過(guò)

北京西直門(mén)立交橋的設(shè)計(jì),經(jīng)常遭人詬病??陀^上講,對(duì)于一座立交橋而言,能夠四通八達(dá),就算得上基本完成任務(wù)了。大家詬病的原因,主要是因?yàn)樾羞M(jìn)路線(xiàn)太復(fù)雜。

當(dāng)然,站在設(shè)計(jì)者角度講,他們需要綜合考慮來(lái)自各方面的制約。但是考慮到世界上立交橋比比皆是,各有各的難處,然而像西直門(mén)立交橋這樣讓人迷惑的,還真是少見(jiàn)。所以,對(duì)于西直門(mén)立交橋的設(shè)計(jì)者而言,困難是客觀存在的,但是改進(jìn)的空間總還是有的。

Figure 10. 北京西直門(mén)立交橋行進(jìn)路線(xiàn)
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/ef82_4113112287_86cfb1cffd_o.png

大型網(wǎng)站的架構(gòu)設(shè)計(jì)也一樣,沿用傳統(tǒng)的設(shè)計(jì),省心又省力,但是代價(jià)是網(wǎng)站的性能。網(wǎng)站的性能不好,用戶(hù)的體驗(yàn)也不好。Twitter這樣的大型網(wǎng)站之所以能夠一飛沖天,不僅功能的設(shè)計(jì)迎合了時(shí)代的需要,同時(shí),技術(shù)上精益求精也是成功的必要保障。

例如,從Mongrel到MemCached之間,需要一個(gè)數(shù)據(jù)傳輸通道。或者嚴(yán)格地說(shuō),需要一個(gè)client library communicating to the memcached server。Twitter的工程師們,先用Ruby實(shí)現(xiàn)了一個(gè)通道。后來(lái)又用C實(shí)現(xiàn)了一個(gè)更快的通道。隨后,不斷地改進(jìn)細(xì)節(jié),不斷地提升數(shù)據(jù)傳輸?shù)男?。這一系列的改進(jìn),使Twitter的運(yùn)行速度,從原先不設(shè)緩存時(shí),每秒鐘處理3.23個(gè)請(qǐng)求,到現(xiàn)在每秒處理139.03個(gè)請(qǐng)求,參見(jiàn)Figure 11。這個(gè)數(shù)據(jù)通道,現(xiàn)在定名為libmemcached,是開(kāi)源項(xiàng)目 [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://alibuybuy-img1011.stor.sinaapp.com/2010/11/7666_4115077218_55c7250d43_o.png

又例如,Twitter系統(tǒng)中用消息隊(duì)列來(lái)傳遞控制信號(hào)。這些控制信號(hào),從插入隊(duì)列,到被刪除,生命周期很短。短暫的生命周期,意味著消息隊(duì)列的垃圾回收(Garbage Collection)的效率,會(huì)嚴(yán)重影響整個(gè)系統(tǒng)的效率。因此,改進(jìn)垃圾回收的機(jī)制,不斷提高效率,成為不可避免的問(wèn)題。Twitter使用的消息隊(duì)列,原先不是Kestrel,而是用Ruby編寫(xiě)的一個(gè)簡(jiǎn)單的隊(duì)列工具。但是如果繼續(xù)沿用Ruby這種語(yǔ)言,性能優(yōu)化的空間不大。Ruby的優(yōu)點(diǎn)是集成了很多功能,從而大大減少了開(kāi)發(fā)過(guò)程中編寫(xiě)程序的工作量。但是優(yōu)點(diǎn)也同時(shí)是缺點(diǎn),集成的功能太多,拖累也就多,牽一發(fā)而動(dòng)全身,造成優(yōu)化困難。

Twitter工程師戲言,”Ruby抗拒優(yōu)化”,(“Ruby is optimization resistant”, by Evan Weaver [14])。幾經(jīng)嘗試以后,Twitter的工程師們最終放棄了Ruby語(yǔ)言,改用Scala語(yǔ)言,自行實(shí)現(xiàn)了一個(gè)隊(duì)列,命名為Kestrel [39]。

改換語(yǔ)言的主要?jiǎng)訖C(jī)是,Scala運(yùn)行在JVM之上,因此優(yōu)化Garbage Collection性能的手段豐富。Figure 12. 顯示了使用Kestrel以后,垃圾回收的滯后,在平時(shí)只有2ms,最高不超過(guò)4ms。高峰時(shí)段,平均滯后5ms,最高不超過(guò)35ms。

Figure 12. The latency of Twitter Kestrel garbage collection [14].
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/2f0a_4115072726_c611955bb2_o.png

RubyOnRails逐漸淡出Twitter,看來(lái)這是大勢(shì)所趨。最后一步,也是最高潮的一步,可能是替換Mongrel。事實(shí)上,Twitter所謂“API Server”,很可能是他們替換Mongrel的前奏。

Twitter的Evan Weaver說(shuō),“API Server”的運(yùn)行效率,比Apache+Mongrel組合的速度快4倍。所謂Apache+Mongrel組合,是RubyOnRails的一種實(shí)現(xiàn)方式。Apache+Mongrel組合,每秒能夠處理139個(gè)請(qǐng)求,參見(jiàn)Figure 11,而“API Server” 每秒鐘能夠處理大約550個(gè)請(qǐng)求 [16]。換句話(huà)說(shuō),使用Apache+Mongrel組合,優(yōu)點(diǎn)是降低了工程師們寫(xiě)程序的負(fù)擔(dān),但是代價(jià)是系統(tǒng)性能降低了4倍,換句話(huà)說(shuō),用戶(hù)平均等待的時(shí)間延長(zhǎng)了4倍。

活著通常不難,活得精彩永遠(yuǎn)很難。得過(guò)不且過(guò),這是一種精神。

Reference,
[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】結(jié)語(yǔ)

這個(gè)系列討論了Twitter架構(gòu)設(shè)計(jì),尤其是cache的應(yīng)用,數(shù)據(jù)流與控制流的組織等等獨(dú)特之處。把它們與抗洪搶險(xiǎn)中,蓄洪,引流,渠道三種手段相對(duì)比,便于加深理解。同時(shí)參考實(shí)際運(yùn)行的結(jié)果,驗(yàn)證這樣的設(shè)計(jì)是否能夠應(yīng)付實(shí)際運(yùn)行中遇到的壓力。

解剖一個(gè)現(xiàn)實(shí)網(wǎng)站的架構(gòu),有一些難度。主要體現(xiàn)在相關(guān)資料散落各處,而且各個(gè)資料的視點(diǎn)不同,覆蓋面也不全。更嚴(yán)重的問(wèn)題是,這些資料不是學(xué)術(shù)論文,質(zhì)量良莠不齊,而且一些文章或多或少地存在缺失,甚至錯(cuò)誤。

單純把這些資料羅列在一起,并不能滿(mǎn)足全景式的解剖的需要。整理這些資料的過(guò)程,很像是偵探辦案。福爾摩斯探案的方法,是證據(jù)加推理。

1. 如果觀察到證據(jù)O1,而造成O1出現(xiàn)的原因,有可能是R1,也有可能是R2或者R3。究竟哪一個(gè)原因,才是真正的原因,需要進(jìn)一步收集更多的證據(jù),例如O2,O3。如果造成O2 出現(xiàn)的可能的原因是R2和R4,造成O3 出現(xiàn)的可能原因是R3和R5。把所有證據(jù)O1 O2 O3,綜合起來(lái)考慮,可能性最大的原因必然是(R1,R2,R3), (R2,R4), (R3,R5) 的交集,也就是R2。這是反繹推理的過(guò)程。

2. 如果反繹推理仍然不能確定什么是最可能的原因,那么假定R2是真實(shí)的原因,采用演繹推理,R2必然導(dǎo)致O4證據(jù)的出現(xiàn)。接下去要做的事情是,確認(rèn)O4是否真的出現(xiàn),或者尋找O4肯定不會(huì)出現(xiàn)的證據(jù)。以此循環(huán)。

解剖網(wǎng)絡(luò)架構(gòu)的方法,與探案很相似。只讀一篇資料是不夠的,需要多多收集資料,交叉印證。不僅交叉印證,而且引申印證,如果某一環(huán)節(jié)A是這樣設(shè)計(jì)的,那么關(guān)聯(lián)環(huán)節(jié)B必然相應(yīng)地那樣設(shè)計(jì)。如果一時(shí)難以確定A到底是如何設(shè)計(jì)的,不妨先確定B是如何設(shè)計(jì)的。反推回來(lái),就知道A應(yīng)該如何設(shè)計(jì)了。

解剖網(wǎng)站架構(gòu),不僅有益,而且有趣。

Figure 13. Sherlock Holmes,福爾摩斯探案
Courtesy?http://alibuybuy-img1011.stor.sinaapp.com/2010/11/be50_c2053.jpg

【全文完】

本文來(lái)自:http://www.tektalk.org

原文鏈接:

  1. 解剖Twitter 【1】萬(wàn)事開(kāi)頭易
  2. 解剖Twitter 【2】三段論
  3. 解剖Twitter 【3】Cache == Cash
  4. 解剖Twitter 【4】抗洪需要隔離
  5. 解剖Twitter 【5】數(shù)據(jù)流與控制流
  6. 解剖Twitter【6】流量洪峰與云計(jì)算
  7. 解剖Twitter 【7】 作為一種進(jìn)步的不徹底
  8. 解剖Twitter 【8】 得過(guò)不且過(guò)
  9. 解剖Twitter 【9】結(jié)語(yǔ)
更多精彩內(nèi)容,請(qǐng)關(guān)注人人都是產(chǎn)品經(jīng)理微信公眾號(hào)或下載App
評(píng)論
評(píng)論請(qǐng)登錄
  1. 目前還沒(méi)評(píng)論,等你發(fā)揮!