Twitter“鯨魚”故障技術(shù)剖析

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

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

當(dāng)Web Server發(fā)生503錯(cuò)誤后,Twitter配置了一個(gè)前端鯨魚的顯示頁面。Twitter對(duì)鯨魚頁面有監(jiān)控體系,當(dāng)每秒超過100個(gè)鯨魚就會(huì)引起報(bào)警。

為什么在單位時(shí)間內(nèi)會(huì)有大量的”fail whale”呢?Twitter成立了一個(gè)小組來專門分析此原因。

1. 分析背景資料

“分析性能問題不是一門科學(xué),而是一門藝術(shù)”。

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

2. Web page請(qǐng)求分解

Twitter的頁面請(qǐng)求后端分成2個(gè)階段,在Twitter內(nèi)部稱為IO phase及CPU phase。IO phase指通過網(wǎng)絡(luò)服務(wù)獲取用戶的關(guān)注關(guān)系及相關(guān)的Tweets。第2階段為CPU phase,指將數(shù)據(jù)聚合、排序及按用戶請(qǐng)求的條件輸出。IO及CPU各自在1天內(nèi)消耗的時(shí)間如下。

從圖上看到,latency增大時(shí)IO是主要瓶頸。IO對(duì)應(yīng)于Network service,因此可以判斷是某個(gè)網(wǎng)絡(luò)服務(wù)性能降級(jí)造成。

3. 深度分析

理想情況是網(wǎng)絡(luò)服務(wù)在應(yīng)答相同參數(shù)的請(qǐng)求消耗時(shí)間應(yīng)該基本相同。但實(shí)際情況并非如此,我們大膽假設(shè)某一網(wǎng)絡(luò)服務(wù)性能下降厲害,于是我們就從統(tǒng)計(jì)分析中去尋找這個(gè)服務(wù),我們看到Memcached的統(tǒng)計(jì)圖表如下

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

可提高的空間及解決思路

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

分析

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

最后分析數(shù)據(jù)Memcached請(qǐng)求分布比例如下

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%

結(jié)論:從上面數(shù)據(jù)來看,調(diào)用熱點(diǎn)和瓶頸主要集中在Get操作

因此回頭取看Twitter頁面執(zhí)行流程代碼,找出優(yōu)化方法見注釋。

get(["User:auth:missionhipster",              # 將昵稱轉(zhuǎn)換成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:...",        # 重復(fù)調(diào)用,可記住
get(["limit:count:login_attempts:...",        # 重復(fù)調(diào)用
get(["user:basicauth:...",                    # 防止解密的優(yōu)化
get(["limit:count:api:...",                   # 請(qǐng)求數(shù)限制
set(["limit:count:api:...",                   # 設(shè)置請(qǐng)求數(shù),大部分情況不需要,為什么?
set(["limit:timestamp:api:...",               # 大部分情況不需要, bug
get(["limit:timestamp:api:...",
get(["limit:count:api:...",                   # 重復(fù)調(diào)用
get(["home_timeline:15460619",                # home_timeline業(yè)務(wù)調(diào)用
get(["favorites_timeline:15460619",           # favorites_timeline業(yè)務(wù)調(diào)用
get_multi([["Status:fragment:json:74736693",  # multi_get所有tweets內(nèi)容

上面這段代碼將17個(gè)請(qǐng)求優(yōu)化成10個(gè),部分重復(fù)調(diào)用通過本地cache避免,另外一些沒必要的調(diào)用直接刪除。通過一個(gè)簡(jiǎn)單的優(yōu)化性能就提高了42%。

結(jié)論

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