深入Facebook訊息應用伺服器

發表於2011-05-05

導讀:原作者Jiakai 是 Facebook Messages 開發小組成員。

要點:

  • 1. Facebook 統一訊息系統(郵件、簡訊、聊天、訊息等);
  • 2. 用 HBase 作為後端儲存設施,每個使用者資料儲存在 HBase 的單獨一行裡,每個實體(資料夾、主題、訊息等等)都儲存在自己的HBase列中;
  • 3. 涉及 HayStack 圖片處理基礎設施;
  • 4. 使用 Apache Lucene 維護反向索引列表;
  • 5. 映象了大約 10% 使用者的實時聊天和收件箱中的資訊到測試叢集中,並通過 dark launch 進行測試。

Facebook Messages 是我們曾經所建立的最具技術挑戰性的一個代表產品。

當我們釋出Facebook Messages 時所提到的是我們需要打造一個專門的應用伺服器來管理其基礎架構。

我們最近討論了訊息後臺和我們如何處理所有來自 email, SMS, Facebook Chat 和 Inbox 的通訊。

今天我們將深入訊息應用伺服器的核心。

應用伺服器的業務邏輯

應用伺服器整合了眾多Facebook服務和保護(shields)來自各種終端的複雜性。它提供了一個簡單介面方便客戶端進行標準訊息處理,包括:建立、讀取、刪除、更新訊息和收件箱。

下面是每一部分的流程。

當建立一個新訊息或回覆訊息時,應用伺服器代表傳送者傳遞訊息到收件人。如果收件人是通過其郵件地址,則伺服器通過HayStack獲得附件(如果有的話),構造HTML主體,並建立一個RFC2822訊息。

輸出流


(點選檢視大圖,下同)

當訊息傳送給使用者時,如果地址是一個回覆處理,伺服器將從存在的郵件地址和傳遞的輸入訊息中獲得正確收件人的資訊。伺服器最終將訊息傳遞到使用者郵箱,執行所有必要的預處理和後期處理過程,並決定基於多個訊號的資料夾和主題的訊息路由。

輸入流

當讀取訊息時,伺服器取得有關使用者郵箱的多個統計,如容量;訊息、主題和回覆數;朋友的數量等。同時也獲得資料夾相關統計和屬性,各種搜尋條件的主題列表(資料夾,屬性,作者,關鍵字等等),主題屬性和這個主題的其它訊息。

當刪除訊息時,伺服器標記要刪除的訊息和主題。一個離線任務具體清除訊息內容。

當更新訊息和主題時,伺服器改變訊息或主題屬性,如讀取和到達狀態,標籤等等。同時也處理多個使用者對主題的訂閱和取消訂閱的請求。

管理群組主題

Facebook Messages 使用一個聊天室模型管理群組訊息主題。使用者能加入(訂閱)和離開(取消訂閱)。

當郵件地址是是這個主題的指定接收者時這個模型是必須的,應用伺服器建立一個回覆處理器,類似聊天室ID。當一個郵件接收者回復了主題,則訊息會被髮送到回覆處理器地址。

為了優化讀取效能和簡化移植和備份處理,訊息主題以一種非規格化(denormalized)的方式儲存。於是每個使用者都擁有一份主題後設資料和訊息的拷貝,伺服器廣播訂閱和取消訂閱事件,在一個分散的方式中同步所有接收者訂閱和回覆處理的主題後設資料。伺服器也管理類似使用者仍舊使用老的收件箱或通過他們的郵件地址訂閱的情形。

快取使用者後設資料

當使用者訪問收件箱時,應用伺服器裝載最常用的使用者後設資料(也稱活動後設資料)並將它們儲存在最近最少使用的快取中(a least recently used cache,也就是LRU演算法)。隨後,同一使用者的請求會通過少量的HBase查詢被快速的處理。

我們需要減少HBase查詢,因為HBase不支援join, 為處理一個讀請求,伺服器可能需要在分開的HBase查詢中查詢多個索引和匹配後設資料和訊息體。HBase的最佳化體現在寫操作而不是在讀取上,使用者行為通常擁有好的時間和地域性(good temporal and spatial locality),於是快取能幫助解決這個問題並提升效能。

我們也在通過減少使用者記憶體佔用和轉移到細粒度模式進而提升快取的有效性方面做出了很多努力。我們能快取5%-10%的使用者量和95%的活躍後設資料快取命中率。我們在全域性的memcache層快取了訪問極為頻繁的資料(如在Facebook首頁顯示沒有閱讀的訊息數)。當新訊息到達時應用伺服器標記快取為(dirties)(注:dirties表示修改了但還沒有寫到資料檔案的資料)。

同步

HBase對事務隔離提供了有限的支援。針對同一使用者的多個更新可能同時發生。為解決它們之間的潛在衝突,我們使用應用伺服器作為使用者請求的同步點。一個使用者在任何給定的時間裡由獨有的伺服器提供服務。這樣,同一使用者請求就可以在應用伺服器中以一種完整孤立的方式(Fashion)同步和執行。

儲存模式

MTA代理特性附件和大量訊息實體,在它們能到達應用伺服器之前被儲存在Haystack中。然而,後設資料,包含索引資料和小的訊息體,它們儲存在 HBase中並由應用伺服器維護著。每個使用者的收件箱都是獨立於任何其它使用者的;使用者資料不會在HBase中/共享(shared)。每個使用者資料儲存在 HBase的單獨一行裡,它包含了以下部分:

後設資料實體和索引

後設資料實體包含收件箱物件屬性,如資料夾、主題、訊息等等。每個實體都儲存在自己的HBase列中。不像關係型資料庫(RDBMS),HBase沒有提供用於索引的本地支援 。我們在應用級維護輔助索引(Secondary Indexes),同樣以鍵/值對的方式儲存在分開的列中。

比如,要回答查詢“loading unread threads on the second page of the Other folder,” 應用伺服器首先搜尋後設資料索引以獲得符合條件的主題列表,然後取出指定主題的後設資料實體,以它們的屬性構造響應。

正如我們前面所提到的,快取和有效的預裝載能減少HBase查詢量以獲得更好的效能。

活動日誌

使用者郵箱中的任何更新(如發表和刪除訊息,標記主題為已讀等等)會立即以時間的順序新增到列中,這稱為一個活動日誌(action log)。小的訊息實體也儲存在活動日誌中。

我們能通過回放(replaying )活動日誌的方式構造或恢復使用者郵箱的當前狀態,我們使用最後活動日誌的ID以後設資料實體和索引的版本回放。當使用者郵箱被載入,應用伺服器比較後設資料版本和最後活動日誌ID,如果後設資料版本滯後(lags behind)則更新郵箱內容。

活動日誌儲存在應用級帶來極大的靈活性:

  • * 我們能通過回放活動日誌無縫切換到一種新的模式並且能通過一個離線的 MapReduce 任務或線上的應用伺服器自身生成新的後設資料實體和索引。
  • * 我們能在一個批處理中執行大量HBase非同步寫以節省網路頻寬和減少HBase壓縮成本。
  • * 它是與其它元件交換永續性資料的標準協議。比如,我們通過將活動日誌寫到記錄日誌(Scribe log)做應用級的備份。這個移植管道轉化使用者老的收件箱資料到活動日誌並且通過離線MapReduce生成後設資料和索引。

搜尋索引

為支援全文檢索,我們維護著一個從關鍵字到匹配訊息的反向索引。當一個新訊息到達時,我們使用 Apache Lucene 去解析和轉化它到一個(keyword, message ID, positions)元組(tuples)中,然後以遞增的方式加入到 HBase 的列中。每個關鍵字都擁有自己的列。所有的訊息,包括聊天記錄,郵件和簡訊都被實時索引。

dark launch 測試

應用伺服器是我們從零開始構建的一個全新軟體,因此在將它推向5億使用者前我們需要監控它的效能、可靠性和伸縮性。我們最初開發了一個壓力測試機器人 (robot)用來生成模擬請求,但是我們發現這樣的結果可能會受到其它一些新因素的影響,如訊息長度,不同型別請求的分發,使用者活躍度的分佈等等。

為了模擬一個真實的產品負荷,我們製作了 dark launch,我們映象了大約10%使用者的實時聊天和收件箱中的資訊到測試叢集中。Dark launches 幫助我們發現更多效能問題和識別瓶頸。我們也使用它作為一個有說服力的指標來評價我們所做的很多改進 。接下來,我們會繼續努力為我們的所有使用者提供嶄新的訊息系統。

相關文章:《揭祕Facebook背後的那些軟體

原文:Jiakai
譯文1:灰狐   譯文2:張鵬

 

相關文章