支撐5億用戶、1.5億活躍用戶的Twitter最新架構詳解及相關實現

如果你對項目管理、系統架構有興趣,請加微信訂閱號“softjg”,加入這個PM、架構師的大家庭


摘要:Twitter出道之初只是個奮鬥在RoR上的小站點,而如今已擁有1.5億的活躍用戶,系統日傳輸tweet更多達4億條,並已完成了以服務爲核心的系統架構蛻變。

Twitter如今在世界範圍內已擁有1.5億的活躍用戶,爲了給用戶生成timeline(時間軸)需支撐30萬QPS,其firehose每秒同樣生成22MB數據。整個系統每天傳輸tweet 4億條,並且只需要5分鐘就可以讓一條tweet從Lady Gaga手中呈現到她3100萬粉絲的屏幕上。當下Twitter系統的規模及強大的吞吐量確實惹人豔羨,然而在出道之初Twitter也只是個奮鬥在 RoR上的小站點而已,下面就一覽Twitter如何完成從RoR到以服務爲核心的系統架構蛻變。

Twitter系統的一些特性:

1. 當下的Twitter已不滿足於Web Ap的現狀。Twitter期望成爲一組API,驅動世界範圍內的移動客戶端,成爲世界級最大的實時事件鏈之一。

2. Twitter主導的是消費機制,而不是生產機制。每秒讀取timeline的操作就會產生30萬次的查詢,而每秒的寫入請求只有6000左右。

3. 離羣值,擁有巨量粉絲的個體開始變得普遍,大量粉絲擁有者發送tweet時會因爲大量的擴散而變得緩慢。Twitter試圖將這個延時控制在5秒內,但是也並非一直生效,特別是名人們發送tweet以及相互轉發變得越來越頻繁後。這樣就導致轉發的內容可能比原始內容先一步到達共同粉絲的界面上,這樣一來,就高價值用戶來說,Twitter的主要精力必須從寫操作轉移到讀操作上。

4. 使用Redis集羣處理Home Timeline(首頁時間軸,包含了衆多關注者的tweet),最大條數爲800。

5. 從你關注的人和你點擊的鏈接,Twitter可以獲知一系列關於你的信息。

6. 用戶最關心的是tweet內容,然而大部分的基礎設施卻和這些內容不相關。

7. 對如此複雜堆棧進行性能追蹤所需求的監視和調試系統往往非常複雜,同樣舊決策的影響會不時的出現。

Twitter面臨的挑戰

1. 1.5億的用戶以及支撐timeline(home及Search)的30萬QPS會讓最初的具體實現(Naive materialization)變得緩慢。

2. 最初的具體實現由大量選擇語句組成,遍及整個Twitter系統,曾今使用後被取締。

3. 使用一個基於寫的擴散方案。在接收到tweet時,系統將做大量的計算以發現tweet需要呈現的用戶。這將造就更快、方便的讀取,不要對讀做任何的計算。由於所有的計算都被安排到寫去執行,每秒大約可處理4000個寫操作,比讀操作要慢一些。

Twitter的團隊合作

1. Platform Service團隊承擔起了Twitter核心基礎設施的一切事務:


  • 他們負責Timeline Service、Tweet Service、User Service、Social Graph Service這些驅動Twitter平臺的所有組件。

  • 內外客戶端使用了大致相同的API

  • 產品團隊不需要擔心任何規模相關

  • 針對第三方API的註冊應用過百萬

  • 做容量規劃,打造可擴展後端系統架構,在網站超出預期增長時要不斷的更換基礎設施。


2. Twitter還擁有一個架構團隊。負責Twitter的整體架構,維護技術負債列表。

Pull和Push模式

1. 任何時刻都有用戶在Twitter上發佈內容,Twitter的任務就是考慮如何將消息同步發出並呈現到粉絲。

2. 真正的挑戰就是實時性約束,目標則是在5秒內將消息發送到粉絲:

  • 交付意味着儘可能快的收集內容、投入互聯網,並且在儘可能短的時間內返回。

  • 交付要做的是發佈到內存timeline集羣、推送通知以及觸發電子郵件,其中包括所有的iOS、黑莓、安卓通知以及SMS。

  • Twitter是最大的SMS製造者

  • Elections可以成爲產生內容並且以最快速度擴散內容的最大動力

3. 兩種類型的timeline:user timeline(用戶時間軸,即指定用戶tweet頁)及home timeline

  • user timeline就是一個指定的用戶發佈的所有tweet

  • Home timeline是你所有關注用戶user timeline的一個臨時合併

  • 業務規則。非你關注人@你時,將會被自動過濾,轉發的tweet也可以被過濾。

  • 在Twitter的規模做這些事情是非常有挑戰性的


Pull模式

1. 指向timeline,比如Twitter.com及hone_line API。之所以將tweet發送給你,是因爲你對其進行了請求。基於Pull的交付:你通過REST API的調用向Twitter請求這些數據。

2. 查詢timeline,搜索API。對資料庫進行查詢,儘可能快的返回所有匹配指定查詢的tweet。

Push模式

1. Twitter運行了一個巨型的實時事件系統,通過Firehose以每秒22M的速度推送tweet。

  • 給Twitter打開一個socket,他們將會在150毫秒內完成所有公共tweet的推送。

  • 任何時候給推送集羣打開的socket都超過1百萬個

  • 使用類似搜索引擎的firehose客戶端,所有公共的tweet都通過這些socket傳輸

2. 用戶流連接。TweetDeck及Mac版的Twitter同樣通過這種方式驅動。在登錄的時候,Twitter會查看你的社交圖,同樣也只會推送關注人的消息,重建home timeline,而不是在持久的連接過程中獲得同一個timeline。

3. 查詢API,發佈一個對tweet的持續查詢時,每當有新的tweet發佈,並且被認定匹配這個查詢,系統會將這條tweet發送給相應的socket。

高等級基於Pull的timeline


  • Tweet由一個寫入API生成,它將會通過負載均衡器及TFE(Twitter Front End)

  • 這種做法很直接,所有的業務邏輯在tweet生成時就已經被執行。

  • 隨着tweet的擴散過程開始,新生成的tweet會被投入一個大規模的Redis集羣中。每個tweet都會在3個不同的機器上做3個拷貝。因爲在Twitter的規模,每天會有大把的機器出故障。

  • 粉絲的查詢基於Flock的社交圖服務,Flock會維護粉絲及粉絲列表:



  • Flock會返回一個接收者的社交圖,並且開始循環訪問所有存儲在Redis集羣上的timeline

  • Redis集羣擁有TB級以上的內存

  • 每次投遞4K左右的tweet

  • Redis使用原生的表結構

  • 如果你有2萬個粉絲,負責粉絲查詢的守護進程將會確認2萬個用戶在Redis集羣中的具體位置,然後它會橫跨整個Redis集羣將Tweet ID插入相應的列表中。所以當你有2萬個粉絲時,每條tweet的寫入都會造成2萬個插入操作。

  • 儲存的信息包括新生成tweet的ID、tweet編寫者ID以及一個4字節大小的狀態信息(轉發、評論或者是其它相關)。

  • Home timeline位於Redis集羣中,每個有800條tweet。如果你向後翻太多頁就沒了,RAM是限制列表tweet數量的最大瓶頸。

  • 爲了控制延時,所有活躍用戶都存儲在內存中。

  • 活躍用戶的定義是在30天內有登陸過Twitter,當然這個規則可以根據緩存容量、實際使用等進行修改。

  • 如果你不是活躍用戶,tweet就不會被放入緩存。

  • 只對home timeline進行存盤(持久化。PS:個人覺得這裏應該是user timeline,如果是home timeline下文的重建方法顯然不科學,歡迎大家討論

  • 如果home timeline不在Redis集羣中,則需要經歷一個重建的過程:



  1. 對社交圖服務進行查詢,找出你關注的人。分別的訪問磁盤獲取每個人的數據,然後將他們送回Redis。

  2. 通過Gizzard使用MySQL處理磁盤存儲,這將抽象出所有SQL事務並且提供了全局備份。



  • 鑑於每條tweet都會做3個備份,如果其中某臺機器發生故障,他們無需對這臺機器上的所有timeline進行重建。

  • 當tweet被轉發時,將會存儲一個指向原tweet的指針。



  • 當做home timeline查詢時,Timeline Service將被調用。Timeline Service確認home timeline究竟存在哪臺機器上:



  • 鑑於timeline備份在3個不同的機器上,所以需要運行3個不同的哈希環。

  • 一旦找着其中一個,就會儘可能快的返回結果。

  • 雖然這個過程會花費稍長的一點時間,但是讀的處理仍然很快。從冷緩存到瀏覽器上呈現大約需要2秒,其中一個API的調用時間大約400毫秒。



  • 鑑於timeline只包含了tweet的ID,所以還必須要做tweet內容的查詢。確定了ID以後,Twitter將通過T-bird並行獲取tweet的內容。

  • Gizmoduck是個用戶服務,而Tweetypie則是個tweet對象服務,每個服務都擁有自己的獨立緩存。用戶緩存使用的是memcache集羣,緩存了所有用戶。Tweetypie處理的是上個月的內容,它將一半的tweet儲存在它獨立的memcache集羣中,當然這個部分服務的是內部用戶。

  • 內容的過濾同樣會省卻一些讀取時間,比如過濾掉法國的納粹相關,這些內容的讀取時間在呈現之前就被過濾了。


高等級的搜索

1. 所有的計算都通過讀來解決,這讓寫更加簡潔

2. 當有tweet生成時,Ingester會做相應的語法分析和索引,隨後會將其傳入Early Bird機器中。Early Bird屬於Lucene的修改版本,同時索引都儲存在內存中。

3. 在tweet擴散過程中,它可能會被儲存在多個home timeline中,其個數由粉絲的數量決定。然而在Early Bird中,一個tweet只會被存入一個Early Bird機器中(不包括備份)。

4. Blender負責timeline的查詢,橫跨整個數據中心做集散操作。它對每個Early Bird做查詢,以發現與查詢條件匹配的內容。如果你搜索“New York Times”,Blender會查詢數據中心的所有分片並返回結果,同時還會做分類、合併及重新排序等。排序的規則基於互動的數據,也就是轉發、收藏及評論的數量等。

5. 互動的信息使用寫的模式完成,這裏會建立一個互動timeline。如果你收藏或者回復一個tweet,將會觸發對互動timeline的修改;類似於home timeline,它同樣由一系列的互動ID組成,比如收藏ID、評論ID等等。

6. 所有這些信息都被送到Blender。以讀的方式進行重算、合併以及分類,返回的結果就是search timeline爲你呈現的界面。

7. Discovery是個基於你相關信息的定製搜索,這些信息主要來自你關注的人、打開的鏈接,而重新排序的規則同樣基於這些信息。

Search和Pull是相反的

1. 搜索和pull看起來非常相似,其實他們有着本質上的區別。

2. 在home timeline情況下:

  • 寫。一個寫tweet的動作會觸發一個O(n)規模的Redis集羣寫入操作,n的值取決於粉絲的數量,由此可見處理Lady Gaga及Barack Obama這樣擁有數千萬粉絲的名人將會很麻煩。Redis集羣上的信息都會寫入磁盤,Flock集羣會將user timeline儲存到磁盤上,但是通常情況下timeline在Redis集羣的內存中都可以發現。

  • 讀。通過API或網絡查找Redis是一個常數規模的操作。Twitter對home tiimeline的讀操作做了高可用性優化,讀操作只花費數十毫秒。這裏也可以看出Twitter主導的是一個消費機制,而不是生產機制。每秒可處理30萬個讀操作,而寫操作每秒處理6000個。

3. 搜索timeline情況:

  • 寫。Tweet生成,並且傳輸到Ingester,只會寫入一個Early Bird機器。一個tweet處理的時間大約爲5秒,其中包括了排隊及尋找待寫入的Early Bird 機器。

  • 讀。每個讀請求都會觸發一個O(n)規模的集羣讀操作。讀大約需要100毫秒,搜索不涉及到存盤。所有的Lucene索引都保存在RAM中,所以聚散是非常有效率的,因爲不涉及到磁盤。

4. Tweet的內容基本上與大多數的基礎設施都是無關的。T-bird存儲了所有tweet內容,大部分的tweet內容都是在內存中。如果沒有的話,可以通過select查詢將其拉回內存。與tweet內容相關的功能非常少,搜索就是其中一個,而Home timeline則完全不關心。

未來的工作

1. 如何將這條數據的管道打造的更快更有效

2. 在5秒內做到tweet的擴散,但是並不是時刻的奏效,特別是越來越多的高粉單位。

3. Twitter是非對稱的關注,只有你關注人的tweet纔會呈現給你。Twitter可以從這些單向關注中獲取你更多的信息,有些單向關注同樣還影射出一些社會契約。

4. 問題一般發生在大基數的圖上:@ladygaga擁有3100萬粉絲,@katyperry擁有2800萬粉絲,@justinbieber擁有2800萬粉絲,@barackobama擁有2300萬粉絲。

5. 大批量粉絲的擁有者每發送一條tweet將造成數據中心大量的寫入操作,而隨着越來越多名人之間的交互,挑戰變得更加的艱鉅。

6. 這些需要擴散給大批量用戶的tweet是Twitter最大的挑戰,在關注這些名人的共同粉絲中,經常會出現回覆tweet比原文更早一步送達的情況。他們在站點中引入競態條件,比如最近關注Lady Gaga的粉絲可能會比老早之前關注的粉絲早5分鐘看到tweet內容。比如說一個用戶先收到了tweet,並進行回覆,然而這時候Lady Gaga的原微博並沒有擴散完畢,這樣就會存在有些用戶先看到回覆的情況,爲用戶造成很大的困擾。Tweet通過ID進行排序,因爲他們大多數是單調遞增的,然而在如此粉絲規模下,這種做法並不奏效。

7. 尋找讀和寫途徑的合併,不再做如此大規模的擴散;比如傳播Taylor Swift新生成的tweet,取代在生成時進行擴散tweet ID,而是在讀取時候就進行合併。通過平衡讀寫途徑,節省百分之幾十的資源。

解耦相關

1. 基於Twitter通過各種途徑傳播tweet,解耦可以讓不同技術團隊獨立完成自己的工作。

2. 基於性能問題,系統也需要解耦。Twitter過去使用的一直是完全同步模式,而在兩年前因爲性能問題他們停用了這個模式。設備接收一個tweet需要145毫秒,接收完畢後就斷開所有客戶端連接,這麼做同樣也因爲技術負債。寫的路徑由Ruby驅動,通過MRI實現,一個單線程服務器,每次Unicorn worker分配都會耗盡所有處理性能。每當有tweet流入,Ruby就會接收它,將它放到一個隊列中然後斷開鏈接。他們在每臺服務器上只運行45-48個進程,這樣的話每個機箱能同時處理的tweet數量也是這麼多,所以他們必須儘可能快的斷開連接。

3. 當下的tweet已經通過異步模式來處理,而這些討論也都是建立在異步之上。

監視相關

1. 系統性能實時儀表盤

2. 使用VIZ系統監視每個集羣,請求Timeline Service並從Scala集羣獲取數據的平均時間爲5毫秒。

3. 基於Google Dapper系統的Zipkin,工程師可以通過Zipkin對請求的細節進行監視,比如獲取請求所訪問的服務及請求時間,這樣就可以獲知每個請求的性能細節。這樣就可以通過每個階段耗費的時間對系統進行調試,同樣也可以從總體上看從請求到交付耗費的時間。花費了兩年的時間,Twitter將活躍用戶的timeline降到2毫秒。

部分統計數據:


  • 如果你有100萬個粉絲,每個tweet將耗費數秒的時間來傳播

  • Tweet輸入統計:每天4億條;日平均統計5000每秒;日統計峯值7000每秒;大事件期間高於1.2萬每秒。

  • Timeline交付統計:每天300億次

    如果你對項目管理、系統架構有興趣,請加微信訂閱號“softjg”,加入這個PM、架構師的大家庭


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章