以190億美元的價格出售給Facebook,交易談判過程中更受到了拉里·佩奇的勸阻,WhatsApp確實取得了一場輝煌的勝利。然而不可忽視的是,該公司用以服務4.5億活躍用戶的工程團隊只有區區32人。近日HighScalability創始人Tod Hoff撰文分析了這一天價收購的原因和WhatsApp的高可靠架構,雖然并不完整,但還是有許多值得學習的地方。
以下為譯文:
從WhatsApp軟件架構師Rick Reed在3月份即將發表的演講介紹中(That's 'Billion' with a 'B': Scaling to the next level at WhatsApp),我們可以得知WhatsApp當下的一些輪廓:
現在,WhatsApp有數百個節點、數千個核心及數百TB的內存,我們的目標是服務全世界數十億用戶。在WhatsApp,我們使用了Erlang/基于FreeBSD服務器基礎設施,為了應對消息的增長需求,我們必須克服一個又一個挑戰,可以說在系統容量(大于8000核心)和速度(每秒Erlang消息超7000萬)上的極限挑戰從未停歇!
鑒于這個演講在3月份才發布,這里我們不妨聚焦Rick Reed兩年前的分享—— WhatsApp如何應對數百萬的同時鏈接數。
在Yahoo!供職時,Rick Reed曾負責用C++建立高性能的消息總線,因此他對高擴展性架構并不陌生。WhatsApp創始人中還包括了一個擁有大量擴展經驗的Yahoo!工程師,不難得知,WhatsApp有現在的成就與兩個創始人的超凡技術是分不開的。而企圖成為全世界50億手機服務商的遠大目標,他們也必須將技術推向極限!
190億美元的交易的背后
做為一個程序員,如果你問WhatsApp是否值那么多,我的答案必然是否定的!因為產品真正價值不在于那些死板的代碼,而在如何讓用戶喜歡并且使用。
著眼這個應用的特點,我們知道WhatsApp是一個世界級的 零噱頭(零廣告,零創意,零游戲)產品,在SMS收費時代提供了一個免費的信息交互平臺。當下,這個應用已被數量巨大的美國人使用,因此一旦有用戶加入,即會在上面發現大量的親朋好友。在手機如此普及年代,WhatsApp絕對稱得上填補了美國社交網絡上的空白。鑒于其跨平臺特性,所以每個人都可以使用它;值得關注的是,它還可以傳遞各種格式信息,比如共享地理位置、發送語音及視頻等。WhatsApp有著非常出色的國際化,使用手機號碼作為驗證信息、聯系人列表作為社交圖,因此你不需要郵箱驗證、用戶名及密碼,更不需要提供信用卡等號碼信息——“it just works”。
雖然WhatsApp有著如此的誘人特性,但是它仍然不值190億美元,而且對于互聯網巨頭來說,這樣的特性模仿起來也并不復雜。
WhatsApp之所以能賣190億美元可能有多個原因: 第1, 如Google CEO Larry Page所說;第2, WhatsApp已經威脅到Facebook;第3,Facebook正在褪色,需要補充大量新鮮血液;第4, WhatsApp是你的電話簿;第5,WhatsApp可以收集到大量的元數據(雖然他們并未保存)。
準確的說,190億美元收購WhatsApp是為了它的 4.5億用戶,為了它的日百萬用戶增加,為了它可能會有10億用戶的潛能——Facebook需要WhatsApp成為下一個10億用戶應用,當然只能成為它的一部分。同時,40美元一個用戶的價格也并不離譜,特別是以股票充當大部分金額。Facebook曾今使用 30美元每用戶的價格收購了Instagram,而 Twitter的每個用戶更值110美元。
Benedict Evans 做了一個非常棒的預測,移動是一個萬億級的市場。WhatsApp通過每天發送180億SMS消息(整個SMS系統每天發送的消息也只有200億條)攪亂了整個SMS市場,而這個市場有著超過1億美元的稅收。在智能手機采用越發普及的年代,WhasApp有著比Facebook更清晰的盈利思路。然而,在承諾了零廣告和零干涉后,Facebook又會從何處得益?
在 移動應用業務開發上有著許多有趣的用例, WhatsApp則經常被項目團隊用來建立會話組,風投們也樂于將它作為交易流程中的對話工具。
Instagram被Kuwait用來出售綿羊。
WeChat,WhatsApp的一個競爭者,在1月份推出了一個叫車服務;值得關注的是,僅僅一個月,這個應用上的 打車成交量就達到了2100萬。
商業活動看起來確實很適合WhatsApp,但是它的用途顯然還有更多——西班牙警察使用它來抓捕罪犯,意大利人們使用它來組織籃球比賽。
鑒于每個人都有手機而且消息應用已變得更加強大、自由及便宜,商業及其它類型應用程序已逐步向移動端過渡。許多功能已不再只能用桌面或Web應用程序實現,消息應用已初露鋒芒。
其中,許多應用已經對Facebook和Google產生威脅,桌面和網絡皆將成為過去, 消息+移動將讓整個系統愈加偏離這些巨頭的王國,那么在這股移動的大潮中Facebook又該何去何從?
隨著移動化的更加深入,Facebook的地位已愈發岌岌可危,用戶的流逝顯然已不可避免。桌面Web是Facebook后端所有特性的入口,它非常的肥胖、復雜及陳舊,當下已經很少有人會喜歡Facebook的UI。
在Facebook確定了“Mobile first”這個移動方案后,更深度的打磨被執行——不僅建立不同用途的應用,同時也提供了多個具有類似功能和相互競爭的應用,這些應用有時甚至不會共享相同的后臺基礎設施,比如:Messenger和WhatsApp,Instagram和Facebook圖片應用。Paper也是Facebook一個限制功能候補入口,但是它表現得非常好。
在Mobile World Congress上,Facebook CEO Mark Zuckerberg發表了自己的看法, 他認為收購WhatsApp與Internet.org的愿景緊密相連:
這個思想就是建立一組基礎互聯網的免費服務——“互聯網的911”,這可能是類似Facebook這樣的社交網絡服務、消息服務以及搜索服務等。將這些服務提供給那些支付得起卻沒有支付意識的用戶,引誘其上癮,讓他們知道自己的重要性,從而更愿意使用更多的收費服務。
這是個長久的目標,這個舞臺里有足夠多的利益去追逐,然而即時回報率卻非常的低, Facebook正在這條道路上前行著
拋開這次交易不談,WhatsApp究竟是如何用32個工程師來支撐4.5億的活躍用戶,下面一起探索……
服務4.5億用戶的高可靠架構
信息源
需要注意的是, WhatsApp的整體架構并未公開,這里僅僅是從不同信息源中獲取不同的片段。Rick Reed的講座主要分享了使用Erlang實現單服務器200萬連接數,雖然很有價值,但是并不是整個應用架構。
- Rick Reed: 擴展到數百萬并發連接數(2012, PPT)
- Rick Reed: Erlang Factory(訪談)
- Eugene Fooksman: WhatsApp的Erlang使用(訪談)
- DLD14: 關于WhatsApp(Jan Koum和David Rowan)
- Yowsup以前是WhatsApp的一個開源API,但是由于 棄用了DMCA當下已不可用,但是同樣可以說明WhatsApp的一些內部工作機制。
- 相關文章中列出的一些信息。
一、 統計
這些統計是當下系統的一些數據,更多針對數據存儲、消息、meta-clustering以及新加入的BEAM/OTP補丁。
- 4.5億的活躍用戶,并且是史上最快達到這個數字的公司
- 32個工程師,平均每人支撐1400萬活躍用戶
- 每天收發跨7個平臺的500億消息
- 平均每天注冊用戶過百萬
- 0廣告開銷
- 800萬投資
- 數百個節點
- 8000+核心
- 數百TB內存
- 每秒Erlang消息超過7000萬
- 在2011年,WhatsApp單服務器取得 100萬個tcp會話,同時還有內存和CPU剩余。在2012年, tcp會話發展到了200萬。2013年WhatsApp 發表tweet聲明,70億消息入站,110億消息出戰,即每天處理180億消息,偉大的2013!
二、 平臺
后端
- Erlang
- FreeBSD
- Yaws、lighttpd
- PHP
- BEAM定制補丁(BEAM類似于Java的JVM,但適用于Erlang)
- 定制XMPP
前端
- 7個客戶端平臺:iPhone、Android、Blackberry、Nokia Symbian 360、Nokia S40、Windows Phone和一個未知的
- SQLite
三、 硬件
標準的面向用戶服務器:
- Dual Westmere Hex-core(24個邏輯CPU)
- 100 GB RAM、SSD
- Dual NIC(公共面向用戶的網絡、私有的后端 /分布)
四、 產品
- 聚焦消息傳遞。連接來自世界各地的用戶,忽視他們的地理位置,無需支付高額費用,創始人Jan Koum還經常提起1992年在世界各地與家里人聯系是多么的難。
- 隱私。由Jan Koum制定,消息不會在服務器上儲存,聊天記錄也不會儲存,目的就是不去了解用戶隱私。不會保存用戶姓名及性別,聊天記錄只存儲在電話上。
五、通用
1. WhatsApp服務器基本上完全使用Erlang實現
- 做后端消息路由的服務器系統使用Erlang實現
- 值得炫耀的是,如此龐大數量的活躍用戶只使用非常少的服務器來管理,團隊一致認為這很大程度上歸功于Erlang。
- 值得注意的是,Facebook Chat就是在2009年使用Erlang開發,他們棄用Erlang的原因是難以招聘到優秀的程序員。
2. WhatsApp服務器最早從Ejabberd開始
- Ejabberd是個非常出名的開源Jabber服務器,使用Erlang實現。
- 最初選用它的原因是開放、廣受開發者關注、易于開始以及Erlang在大型通信系統上的長期口碑。
- 接下來的許多年一直從事Ejabberd的重寫和修改,包括從XMPP轉換到內部開發協議、調整代碼庫以及重設計一些核心組件,對Erlang VM做了大量的修改以獲得高性能。
3. 為了應對每天500億消息,工作重心被放到可靠系統的打造上,貨幣化對于我們來說還是件遙遠的事情。
4. 系統的健康狀況主要看隊列的長度,每個節點上消息隊列的長度都會被一直監控,超過預先設置的臨界值則會發出提醒,多個警報發生則標志著系統進入了下一個瓶頸。
5. 通過上傳圖片、音頻、視頻到一個HTTP服務器上來發送多媒體消息,然后將鏈接與Base64編碼的縮略圖一起添加到內容(如果可用)。
6. 有些代碼基本上每天都在變化,通常情況下是一天幾次;當然,峰值期間必須避開的。Erlang非常適用于將修改或者是新功能添加到產品,熱加載意味著無需重新啟動就可以實現修改,錯誤可以很快的得到解決,同樣通過熱加載,系統變得更加松耦合,這可以讓更新快速的發布。
7. WhatsApp使用了什么樣的協議?WhatsApp服務器池使用了SSL Socket,在客戶端重新連接對消息進行檢索之前,所有消息都會在服務器上排隊。消息的成功檢索會發回給WhatsApp服務器,它將會被重新轉發給原始發送者;一旦客戶端成功接收這條消息,它就會在服務器存儲中擦除。
8. WhatsApp注冊程序的內部工作機制是什么樣的?WhasApp依賴電話IMEI號碼來建立用戶名/密碼,這點在最近已經修改。WhatsApp現在會讓應用發送一個包含5位數Pin的一般請求,然后給這個電話號碼發送一個SMS,這意味著WhatsApp客戶端不再受限于某臺手機。基于Pin的號碼,應用會從WhatsApp請求一個唯一的鍵,這個鍵將作為未來的使用密碼,這同樣意味著在新的設備上注冊后會無效原有設備上的鍵。
9. 在Android上使用了Google的推送服務。
10. 在Android上有更多的客戶。與Android打交道更讓大家愉快,開發者能夠快速的基于一個特性構建原型,并以最短的時間推出,如果存在問題的話也可以快速修復,iOS則不行。
六、單服務器上200萬連接數的探索
1. 雖然用戶增長是喜聞樂見的,但是它同樣意味著你得投入更多錢去購買硬件;同時,機器數量的增加也大幅增加了管理和運維復雜性。
2. 需要為流量的起伏做規劃,例子就是西班牙的足球比賽和墨西哥的地震。這些現實世界中發生的大事件造成了非常高的流量峰值,因此需要有足夠的剩余容量來應對流量高峰+突發事件,比如一場近期的足球比賽產生了當天35%的出站消息。
3. 初始的服務器負載是每個服務器20萬并發連接。
- 預期將會添加大量的服務器來維持用戶增加。
- 服務器會因為負載的爆發而宕機,網絡及其它的故障也會發生。這時候需要做一些組件的解耦,這樣一來可以添加容量以應對峰值。
- 目標是單服務器支撐百萬連接數,這個目標在只有20萬連接數時已經制定。動態的容量規劃以應對世界級事件、硬件故障及其它類型的小故障,系統需要足夠的彈性去應對高使用率和故障。
七、用來增強可擴展性的工具和技術
1. 編寫系統活動報告工具(wsar):
- 記錄整個系統狀態,包括OS狀態、硬件狀態、BEAM狀態。這是為了便于從其他系統獲取狀態信息,如虛擬內存。跟蹤記錄CPU利用率、系統整體利用率、用戶時間、系統時間、中斷時間、上下文切換、系統調用、traps、數據包發送和接收、所有進程隊列中總消息數、繁忙的端口事件、通信速率、字節輸入/輸出、調度狀態,垃圾回收狀態等。
- 最初一分鐘運行一次,當系統運行變得困難時,時間段將降為1秒鐘一次,因為一分鐘無法運行的情況很少發生。了解所有系統運行情況需要非常細粒度的統計數據。
2. CPU(pmcstat)中的硬件性能計數器:
通過查看CPU時間占用百分比,可以了解正在執行的模擬器(emulator)周期時間。假如是16%,說明只有16%的時間執行模擬代碼,所以即使你能消除所有Erlang代碼的執行時間,也只能節省總運行時間16%,這意味著你應該將重點放在其他方面以提高系統的效率。
3. dtrace、 內核鎖計數、fprof
- Dtrace是主要用于調試,而不是提高性能。
- 在FreeBSD上給BEAM打補丁加入CPU時間戳。
- 寫腳本創建所有進程的聚合視圖,查看哪些程序一直在占用系統資源。
- 最大的勝利是給模擬器啟用鎖計數。
4. 一些問題:
- 早期發現很多時間花在了垃圾回收程序上,這個問題已經被解決了。
- 發現一些網絡堆棧的問題,后來堆棧被調走了。
- 大多數問題是關于模擬器的鎖沖突,主要體現在鎖計數的輸出上。
5. 度量
- 綜合的工作負載,這意味著從你自己的測試腳本中生成流量,這對極限規模下面向用戶系統調優沒有價值。
- 對于用戶表這樣簡單的接口效果不錯,生成接入然后盡可能快地讀取。
- 假如在一臺服務器只支持100萬連接,那么整個系統將需要30臺這樣的主機去打開足夠的IP端口,生成足夠多的連接;然而,這么多的開銷僅僅可以測試一臺服務器。對于測試200萬個連接的服務器需要用到60個主機,生成這樣的規模真的很難。
- 很難生成生產環境下的那種流量。在正常的工作量下還可以估算,但在現實中看到的那些網絡事件、世界事件,這主要因為多平臺上客戶端的不同用戶行為,而且不同國家之間也有差異。
- Tee’d 工作負載
- 采用正常生產環境流量,然后把它放到一個單獨的系統中。
- 這對系統非常有用,因為產生的副作用會受到限制。不想看到網絡擁堵,或者對用戶造成影響。
- Erlang支持熱加載,所以可以在完整生產負荷下產生想法、編譯,在程序運行時加載變化,而且能即時看到變化的好壞。
- 添加旋鈕動態調節生產加載,觀察它對性能的影響。觀察特征輸出,比如CPU使用率、VM 使用率、監聽隊列溢出并調節旋鈕,看看系統會有怎樣的反應。
- 真正的生產負載
- 最終測試。輸入工作和輸出工作都要測試。
- 多次將服務器放入DNS中,使其得到正常情況兩倍或三倍的流量。TTL事務會產生問題,因為客戶端不會遵守DNS TTL,而且這里還會有延遲,因此無法快速做出反應以獲得更多可以被處理的流量。
- IPFW. 將一臺服務器的流量轉移給另一臺,這樣可以使主機的連接數達到理想的水平。內核如果因為有個Bug就奔潰是不行的。
6. 結果
- 開始時每個服務器有20萬個并發連接。
- 第一個瓶頸出現每臺服務器42.5萬個連接的時候。系統遇到了很多沖突,工作停止了。安裝調度器檢測有多少有用的任務被停止、睡眠,或回轉了。在加載時,它開始遇到睡眠鎖,整個系統只用35-45%的CPU利用率,但調度程序的CPU利用率卻達到了95%。
- 第一輪修復使連接數超過100萬個。
- VM利用率為76%,CPU利用率為73%,BEAM模擬器利用率為45%,與用戶百分比很吻合,這是件好事,因為模擬器得和用戶一樣。
- 通常CPU利用率并不是好的評估方法,因為可能由于調度程序使用CPU導致系統看起來很忙。
- 一個月以后解決了瓶頸,每個服務器連接數達到200萬個。
- BEAM利用率為80%,與FreeBSD開始分頁的情況接近。CPU利用率大致相同,有兩倍的連接數。調度程序遇到了沖突,但運行得很好。
- 看來測試可以暫停了,這時開始分析Erlang代碼。
- 最初每個連接有兩個Erlang進程,消減為一個。
- 用計時器完成一些工作。
- 在每個服務器有280萬連接時達到頂峰
- 571k pkts/sec, >200k dist msgs/sec
- 做一些內存優化,VM加載下降到70%。
- 嘗試過將連接數增加到300萬,但沒有成功。
- 當系統遇到故障時,查看長消息隊列(單個消息隊列或消息隊列總和)。
- 將每個進程的消息隊列統計添加到BEAM設備上。包括發送/接收了多少條消息以及發送/接收的速度。
- 每10秒取樣一次,可以看到一個進程有60萬條消息,每15秒延遲出列4萬條消息。預計完全出列時間是41秒。
7. 一些發現:
- Erlang + BEAM + 它們的補丁——可以具有接近線性的SMP可擴展性。在24路服務器上運行系統,CPU利用率達到85%,持續運行負載——它可以像這樣運行一整天。
- Erlang程序模型的證明。
- 服務器使用的時間越長,其積累長時間運行連接就越多,但不是每個連接都很忙碌,其中大多數是閑置的,所以服務器使用時間越長能夠處理的連接數也就越多。
- 沖突是最大的問題。
- Erlang代碼中的一些修復可以減少BEAM的沖突問題。
- 向BEAM添加一些補丁。
- 分區負載工作不需要頻繁跨處理器運行。
- Time-of-day鎖。每次從一個端口發送消息都會針對所有調度程序產生一個Time-of-day鎖,這意味著所有的CPU都會遇到同一個鎖。
- 優化計數器的使用,移除bif計數器
- 發現IO時間表算術增長。創建VM抖動使哈希表在不同的時間點重新分配,改進使用幾何分配表。
- 通過你已經打開的端口添加寫入文件,以減少端口沖突。
- Mseg分配是所有分配器沖突的交點,因此創建好每一個調度程序。
- 獲得一個連接時會有很多端口事務,設置選項降低昂貴的端口交互。
- 當消息隊列積壓太多的話,垃圾回收會破壞系統穩定性。所以暫停 GC,直到隊列收縮。
- 避免一些不必要的麻煩。
- 從FreeBSD 9移植一個TSE計時器到FreeBSD 8。讀取計時器開銷更小,快速時間,比讀取芯片還要便宜。
- 從FreeBSD 9移植igp網絡驅動程序,因為多個隊列會因為NIC鎖定出問題。
- 增加文件和套接字的數目。
- Pmcstat顯示很多時間被用來在網絡堆棧中查找PCB,所以擴大哈希表讓查詢更快些。
- BEAM補丁
- 之前提到過的設備補丁。植入設備調度程序用來獲取使用信息、信息隊列統計信息、sleep數、發送率、消息數等。可以在Erlang代碼中使用procinfo(任務管理)實現,但有100萬的連接時,這一過程會變得非常慢。
- 統計數據收集非常高效,所以它們可以在生產中運行。
- 統計數據保持3個不同衰變間隔:1秒、10秒和100秒。允許隨時觀測發生的問題。
- 讓鎖計數為更大的異步線程計數工作。
- 為調試鎖計數器添加調試選項。
- 調試
- 設置低調度程序的喚醒值,因為調度程序一旦進入睡眠就再也無法喚醒。
- mseg分配器優于malloc。
- 每個調度程序每個實例都有個分配器。
- 配置大的carrier,而且還會越來越大。導致FreeBSD使用超級頁,降低 TLB thrash比率,并為相同的CPU提高了吞吐量。
- 以實時優先級運行BEAM,這樣其他的東西比如cron作業就不會打斷調度程序。防止小故障導致重要用戶通信的阻塞。
- 打補丁下調spin數,從而使調度程序不會spin。
- Mnesia
- 相比erlang:now,更喜歡os:timestamp。
- 不使用事務,用遠程的備份,并行復制每個表以提高吞吐量。
- 事實上還對許多地方進行了修改。
八、經驗總結
1. 優化是件非常艱辛的事情,也只有工程師去做。Rick在回顧大量的修改后(使每個服務器連接數達到200),更覺得頭皮發麻。大量的工作包括編寫工具、運行測試、增加補丁、把讓人眼花繚亂的方法添加到堆棧的每一層、調試系統、尋找蛛絲馬跡,每一個細節都不能放過,你需要努力讓一切都在掌握之中。只有這樣才能消除瓶頸,提高性能以及最大程度地實現可擴展性。
2. 獲取你需要的數據;編寫工具;為工具添加補丁;添加調控旋鈕。擴展系統獲取更多數據是Ken不懈的追求,為了獲取他們需要的數據,需要不停地編寫工具、腳本來管理和優化系統。為了數據,不惜一切代價。
3. 度量;消除瓶頸;測試;不斷重復這樣的過程。枯燥無聊,但你需要這樣做。
4. Erlang很給力!Erlang繼續證明其作為一個多用途、可靠、高性能平臺的優良品質。雖然Erlang也需要大量的調整和修補,這些工作難免會讓人對Erlang產生質疑。
5. 破解病毒式代碼,獲得利潤。“病毒式”現在是優良品質的代名詞,就像WhatsApp那樣,只要你真得做到了,那意味著你得到了很多很多錢。
6. 價值和員工數現在已經沒有直接聯系了。如今,員工的數量并不能說明什么。先進的世界級電信基礎設施使WhatsApp這樣的應用程序成為可能。如果WhatsApp還需要做網絡或手機等設備,那可能根本就不會有WhatsApp這樣的公司存在。功能強大、價格廉價的硬件和開源的軟件也無疑使WhatsApp的成功事半功倍。換句話說WhatsApp的成功在于它在正確的地點、時間為正確的用戶提供了正確的產品。
7. 能夠重視用戶想法是很了不起的。WhatsApp 將自身定位成一個簡單的消息傳遞應用,而不是游戲網絡、廣告網絡或者已經面臨消亡的照片網絡,這一點很重要。這樣的定位使他們沒有在應用中添加廣告,他們努力保持應用簡單的同時添加新功能,傻瓜型操作方式使WhatsApp適用于每一個用戶。
8. 考慮到簡單性,有一些限制是允許的。你的身份被綁定到電話號碼,所以如果你更改了電話號碼你的身份就失效了。這和一般的應用程序確實有點不太一樣,但卻使整個系統在設計上變得更加簡單了。
9. 年齡上的歧視。2009年,因為年齡歧視,WhatsApp創始人Brian Acton在Twitter和Facebook連一份工作都找不到,那就讓它們后悔去吧。
10. 先從簡單的開始然后再深度定制。聊天剛推出時,服務器端基于jabberd,現在它已經被完全重寫,但那個確實是Erlang方向上的第一步。Erlang初次使用時就體現出的可擴展性、可靠性和可操作性,這使得到了越來越廣泛的應用。
11. 保持低的服務器數量。努力讓服務器盡可能的少,同時為短暫高峰期預留足夠的上升空間。分析并優化直到達到收益遞減點,然后再部署更多的硬件。
12. 有目的地增加冗余服務器。這可以確保在公司放假時也能為用戶提供不間斷的服務,員工可以享受假期而不用花時間修復過載問題。
13. 賺錢時也要考慮公司的成長。WhatsApp免費時成長是最快的,早期每天都有1萬次的下載量。然后轉向付費時,下載量下降至每天1000次。在年底增加了圖片消息后,他們就把按下載次數付費改成了按年收費。
14. 靈感總來自最意想不到的地方。忘記Skype用戶名和密碼的經歷無疑給WhatsApp帶來了靈感。