Twitter“鯨魚”故障技術剖析

Monday, Mar 8th, 2010 by Tim | Tags: ,

很多人都熟悉Twitter訪問故障時候那條白色的鯨魚。今年新推出的Twitter Engineering Blog講述了Twitter白鯨技術故障的原因及解決思路。這是到目前為止Twitter公開的最底層的一篇技術資料。
http://engineering.twitter.com/2010/02/anatomy-of-whale.html

當Web Server發生503錯誤后,Twitter配置了一個前端鯨魚的顯示頁面。Twitter對鯨魚頁面有監控體系,當每秒超過100個鯨魚就會引起報警。

為什么在單位時間內會有大量的”fail whale”呢?Twitter成立了一個小組來專門分析此原因。

1. 分析背景資料

“分析性能問題不是一門科學,而是一門藝術”。

鯨魚頁面實際上是對HTTP 503錯誤的前端展示,503錯誤通常是調用后臺請求超時產生,為了避免用戶長時間等待,Twitter的前端(Tim: 也可能是HTTP反向代理)給請求加了超時,避免用戶無限制的等待。超時通常是由于單位時間內訪問的用戶數過大,也有可能是后臺某個服務突然變慢造成。
由于Twitter網站每個時刻都有海量的數據流過,因此要簡單的定位并解決此問題并不容易。

2. Web page請求分解

Twitter的頁面請求后端分成2個階段,在Twitter內部稱為IO phase及CPU phase。IO phase指通過網絡服務獲取用戶的關注關系及相關的Tweets。第2階段為CPU phase,指將數據聚合、排序及按用戶請求的條件輸出。IO及CPU各自在1天內消耗的時間如下。

從圖上看到,latency增大時IO是主要瓶頸。IO對應于Network service,因此可以判斷是某個網絡服務性能降級造成。

3. 深度分析

理想情況是網絡服務在應答相同參數的請求消耗時間應該基本相同。但實際情況并非如此,我們大膽假設某一網絡服務性能下降厲害,于是我們就從統計分析中去尋找這個服務,我們看到Memcached的統計圖表如下

4. Memcached 竟然是鯨魚故障的直接原因

可提高的空間及解決思路

  1. 從上圖看,Memcached在 latency高峰的性能比低谷相差一倍,因此最簡單的判斷是增加硬件即可提高50%的性能。
  2. 另外一種思路就是優化Memcached程序,判斷程序熱點和瓶頸并進行優化。

分析

  1. 通過 Google perf-tools project 工具來分析, http://code.google.com/p/google-perftools/ http://github.com/tmm1/perftools.rb
  2. 通過自己些的一段分析代碼來監控 http://github.com/eaceaser/ruby-call-graph
  3. 通過上面工具的call graph來分析熱點和瓶頸

最后分析數據Memcached請求分布比例如下

get         0.003s
get_multi   0.008s
add         0.003s
delete      0.003s
set         0.003s
incr        0.003s
prepend     0.002s

get         71.44%
get_multi    8.98%
set          8.69%
delete       5.26%
incr         3.71%
add          1.62%
prepend      0.30%

結論:從上面數據來看,調用熱點和瓶頸主要集中在Get操作

因此回頭取看Twitter頁面執行流程代碼,找出優化方法見注釋。

get(["User:auth:missionhipster",              # 將昵稱轉換成uid
get(["User:15460619",                         # 獲取user object(用于檢查密碼)
get(["limit:count:login_attempts:...",        # 防止密碼字典攻擊
set(["limit:count:login_attempts:...",        # 大部分情況不需要, bug
set(["limit:timestamp:login_attempts:...",    # 大部分情況不需要, bug
get(["limit:timestamp:login_attempts:...",
get(["limit:count:login_attempts:...",        # 重復調用,可記住
get(["limit:count:login_attempts:...",        # 重復調用
get(["user:basicauth:...",                    # 防止解密的優化
get(["limit:count:api:...",                   # 請求數限制
set(["limit:count:api:...",                   # 設置請求數,大部分情況不需要,為什么?
set(["limit:timestamp:api:...",               # 大部分情況不需要, bug
get(["limit:timestamp:api:...",
get(["limit:count:api:...",                   # 重復調用
get(["home_timeline:15460619",                # home_timeline業務調用
get(["favorites_timeline:15460619",           # favorites_timeline業務調用
get_multi([["Status:fragment:json:74736693",  # multi_get所有tweets內容

上面這段代碼將17個請求優化成10個,部分重復調用通過本地cache避免,另外一些沒必要的調用直接刪除。通過一個簡單的優化性能就提高了42%。

結論

  1. 在前文2010年的技術架構建議中 提過Cache已經是Web 2.0系統核心元素。從Twitter的故障案例來看Memcached竟然成為了瓶頸并導致了Twitter服務的不穩定。由于在social應用中 cache核心化的設計,“RAM is the new disk”,在cache廣泛使用后也變得調用成本增加,需要考慮進行系統的規劃減少不必要的調用。避免開發人員在代碼中隨意使用cache
  2. 如何定位瓶頸,可以借鑒Google perf-tools項目及上面其他分析工具的思路。
  3. Twitter頁面執行流程值得參考
  4. 整個故障流程分析圖如下