深入 Facebook 消息應用服務器

發布時間: 2011-05-05 13:41  閱讀: 2492 次  推薦: 0   原文鏈接   [收藏]  

  要點:

  1. Facebook 統一消息系統(郵件、短信、聊天、消息等);
  2. 用 HBase 作為后端存儲設施,每個用戶數據存儲在 HBase 的單獨一行里,每個實體(文件夾、主題、消息等等)都存儲在自己的HBase列中;
  3. 涉及 HayStack 圖片處理基礎設施;
  4. 使用 Apache Lucene 維護反向索引列表;
  5. 鏡像了大約 10% 用戶的實時聊天和收件箱中的信息到測試集群中,并通過 dark launch 進行測試。

clip_image001

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

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

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

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

  應用服務器的業務邏輯

  應用服務器集成了眾多Facebook服務和保護(shields)來自各種終端的復雜性。它提供了一個簡單接口方便客戶端進行標準消息處理,包括:創建、讀取、刪除、更新消息和收件箱。

  下面是每一部分的流程。

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

  輸出流 
clip_image002

  當消息發送給用戶時,如果地址是一個回復處理,服務器將從存在的郵件地址和傳遞的輸入消息中獲得正確收件人的信息。服務器最終將消息傳遞到用戶郵箱,運行所有必要的預處理和后期處理過程,并決定基于多個信號的文件夾和主題的消息路由。

輸入流 
clip_image003  當讀取消息時,服務器取得有關用戶郵箱的多個統計,如容量;消息、主題和回復數;朋友的數量等。同時也獲得文件夾相關統計和屬性,各種搜索條件的主題列表(文件夾,屬性,作者,關鍵字等等),主題屬性和這個主題的其它消息。

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

  當更新消息和主題時,服務器改變消息或主題屬性,如讀取和到達狀態,標簽等等。同時也處理多個用戶對主題的訂閱和取消訂閱的請求。 
clip_image004  管理群組主題

  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的單獨一行里,它包含了以下部分: 
clip_image005  元數據實體和索引

  元數據實體包含收件箱對象屬性,如文件夾、主題、消息等等。每個實體都存儲在自己的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 幫助我們發現更多性能問題和識別瓶頸。我們也使用它作為一個有說服力的指標來評價我們所做的很多改進。接下來,我們會繼續努力為我們的所有用戶提供嶄新的消息系統。

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

  英文來源:Inside Facebook Messages’ Application Server

0
0
 
標簽:Facebook
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()