Twitter“鯨魚”故障技術(shù)剖析
很多人都熟悉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 竟然是鯨魚故障的直接原因
可提高的空間及解決思路
- 從上圖看,Memcached在 latency高峰的性能比低谷相差一倍,因此最簡(jiǎn)單的判斷是增加硬件即可提高50%的性能。
- 另外一種思路就是優(yōu)化Memcached程序,判斷程序熱點(diǎn)和瓶頸并進(jìn)行優(yōu)化。
分析
- 通過 Google perf-tools project 工具來分析, http://code.google.com/p/google-perftools/ http://github.com/tmm1/perftools.rb
- 通過自己些的一段分析代碼來監(jiān)控 http://github.com/eaceaser/ruby-call-graph
- 通過上面工具的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é)論
- 在前文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
- 如何定位瓶頸,可以借鑒Google perf-tools項(xiàng)目及上面其他分析工具的思路。
- Twitter頁面執(zhí)行流程值得參考
- 整個(gè)故障流程分析圖如下