服務器非業餘研究http://blog.csdn.net/erlib 做者Sunfacephp
估計不少同窗看到這裏都會以爲迷惑,go的大名已經如雷貫耳了,可是erlang?這個東東是神馬?難道是編程語言?怎麼歷來沒據說過。程序員
這裏請容許我先介紹一下使用Erlang開發的比較有名的應用:golang
一:whatsappweb
只憑32個技術人員,如何應付4.5億的用戶?對於剛剛被Facebook用190億美圓收購的WhatsApp來講,答案是Erlang——一種誕生於上世紀80年代的編程語言,終於在此時走到了聚光燈下。算法
這個應用把erlang的特性發揮到了極致,利用到了它最好的vm、 集羣基礎設施、數據庫mnesia, 消除了很是多的數據Scale、內存池和鎖的問題, 提到的技術和修正點很是值得咱們參考。數據庫
雖然大部分的解決方法咱們在平常都差很少用過。可是他很系統的整理出來,用在商業系統了,這是個很是大的飛躍。編程
能夠服務4.5億用戶的高可靠架構:後端
須要注意的是, WhatsApp的總體架構並未公開,這裏僅僅是從不一樣信息源中獲取不一樣的片斷。Rick Reed的講座主要分享了使用Erlang實現單服務器200萬鏈接數,雖然頗有價值,可是並非整個應用架構緩存
這些統計是當下系統的一些數據,更多針對數據存儲、消息、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年WhatsAppf發表twriter聲明70億消息入站,110億消息出戰,即天天處理180億消息,偉大的2013!
二百多萬的長鏈接push服務器:
whatsapp數據集mnesia的規模:
生產系統的數據:
每秒的消息數:
發展歷程:
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請求一個惟一的鍵,這個鍵將做爲將來的使用密碼,這一樣意味着在新的設備上註冊後會無效原有設備上的鍵。
結果
開始時每一個服務器有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設備上。包括髮送/接收了多少條消息以及發送/接收的速度。
二:RabbitMq
這個相信你們都據說過,世界上最好的企業消息隊列系統之一。
三:Web框架
Mochiweb,CowBoy等
四:電信級別的應用
愛立信等電信公司
五:遊戲服務器領域的大範圍應用
特別是在頁遊和手遊領域,erlang簡直如魚得水,用erlang開發出的千萬級流水遊戲也是數不勝數
六:數據庫
CouchDB,Riak等
七:其餘領域的應用
目前據我所知,在銀行業務,醫療業務,雲業務領域均可以看到erlang活躍的身影.
爲何我要選擇Erlang呢?
1、erlang特別適合中小團隊創業:
erlang有異常成熟、通過電信級別大規模驗證的OTP應用庫,只須要很簡單的代碼就能創建起異常穩定、容錯性強、擴展性強、高併發的服務器框架,這也是erlang最寶貴的核心價值所在。
2、erlang是天生的併發語言:
erlang的併發特性是語言級別的,從開發伊始就採用了CSP併發模式, 以進程爲單位,進程間沒有共享內存,變量不可變的實現方式保證了無鎖的併發模型,所以也是異常高效的,換句話說:你只要像日常同樣寫代碼就能併發,徹底不用操心任何底層實現,你的代碼能完美的並行運行在多核服務器上,若是你能寫出漂亮的併發級別的算法和代碼(儘可能少的順序代碼),那在32核機器上就能跑出32倍性能!!!! Go 語言的併發模型也是取經於Erlang,可是我認爲Erlang的併發模型更優秀,由於進程間徹底沒有共享內存,徹底無鎖。
3、再介紹下我當初的業務需求:
一款多人在線遊戲,一個玩家走一步都要把消息廣播給同屏的玩家,玩家聊天,戰鬥更涉及到大量的消息廣播;如何應對?再有一個及其普通卻不太容易搞定的的需求:在線玩家列表怎麼實現?是啊,你是否是在想用哪一種鎖合適?提到的兩個場景的關鍵詞是:高併發,大量廣播;可能你還會想到"鎖".
我嘗試過在.net下使用完成端口+TPL庫+protocol buffer來完成上面的功能,可是並無經過測試的檢驗,測試模型是聊天.在收發消息方面,客戶端和服務器一對一的收發壓力不大,可是一旦開啓廣播,壓力一下就上去了.對象的頻繁建立會致使垃圾回收,而垃圾回收會致使CPU和內存都飄忽不定,中間加入對象池會獲得必定緩解,可是不能完全解決問題,而後想到的就是人爲干預垃圾回收,判斷標準是什麼呢?那就是用PerformanceCounter吧,結果發現PerformanceCounter一次調用分配的內存至關大!最後一版的結果是:聊天室模型,一人說話廣播給全部人,300人在線可以穩定,人數一多就開始不淡定了.這些都是通過量化分析得出的結果,使用的工具是Visual Studio2010中的Performace Profile工具.
須要解決的第二個問題就是併發加鎖,最簡單的測試模型就是在線玩家列表.這個問題一樣困擾了我好久,嘗試各類鎖,仍是在拋異常,要麼就是性能的降低,問題此起彼伏.後續還要解決TCP通訊的數據格式,以及粘包等問題......
項目時間緊張,存在的風險不少,要儘快把技術方案肯定下來而後去推動別的事情;可是可供選擇的方案有C++和Erlang.坦白講我和團隊的基礎若是使用C++方案,必定能搞出來,可是排錯和性能優化將是一個巨大的挑戰.那麼Erlang呢?從開篇引用的那段文字看,好像這就是我須要的,簡單瞭解了一下語法,仍是很驚喜,因爲以前對F#有過接觸,一下感受很親切.並且我特別關注到:
優勢:
1.面向併發,有成熟並且久經考驗的框架可供使用,網絡部分已經通過了良好封裝
2.內存緩存解決方案進程字典,前者的讀寫速度是50NS-100Ns級別的
3.對二進制數據解析的語法是直觀,簡單,強大(遊戲中有大量的二進制數據要處理
4.沒有共享內存! 沒有鎖!(咱們在代碼中沒有過顯示使用鎖)
缺點
1.從一種語言過渡到另外一種語言,會有各類不爽:
2.控制邏輯簡單隻有if 和 case ,並且有if沒有else,沒有continue break goto
3.包括kernel庫和standlib庫在內,不少函數和變量的命名和傳統語言不同
所以咱們就決定了採用erlang來從新寫一套全新的架構,事實證實當初的決定是無比正確的,一個極少須要重啓、能熱更、穩定的遊戲服務器實在是過重要了,並且開發過程和維護是如此的快速和輕鬆,咱們的團隊一致認爲:歷來沒有想過開發會是這麼一件愉快的事情!
既然Erlang已經被我「吹」的快飛起來了,爲何還要使用Go?
鑑於Go語言已經婦孺皆知了,我也就不介紹了,大概說說我本身的狀況,我這人沒啥其餘興趣愛好,業餘時間絕大部分都花費在所謂的「程序員要不停的學習纔不會落伍」上,所以在11年的時候,知道了go,斷斷續續學習了一年後,Go1.1版本出來後,發現改進很大,就開始認真研究並常年混跡在google-group及國外大牛的博客世界中,自我感受還能夠。固然我絕對不是Go的「朝聖者」,也發現Go確實不是很是完美,具體能夠參見「爲何我要放棄Go「,此文做者的觀點我雖然不敢徹底苟同,可是有些觀點仍是贊同的,好比說不少Go愛好者是很是護短的,若是你敢說什麼「壞話」,就等着被查水錶吧 ;)。
因爲Erlang和Go都是很是棒的語言,這裏就出現一個問題:二選其一仍是物盡其用?通過深思熟慮後,我和團隊選擇了後者。首先,erlang的OTP寫服務器併發框架很是之簡單、穩定且高性能,erlang的Mnesia數據庫也是很輕量:速度很快,分佈式簡單,使用起來也很原生態(是Erlang標準庫支持的),全部的這些都能把程序員從繁瑣的工做中解放出來,可是,erlang也有個挺重要的問題(在不一樣業務場景中此問題也許很突出,也可能徹底可有可無,至少85%的狀況下不算一個問題):它是虛擬機語言,對於順序代碼的執行速度只有C的七分之一,雖然能夠利用多核的優點,可是在大型mmorpg中,消息密集時,CPU的瓶頸仍是挺明顯的,會影響玩家順暢的體驗感受(ARPG)。
所以我就想若是邏輯這部分用Go來寫,是否是能夠很好的利用這兩個語言的優勢進行互補?心動不如行動,因爲咱們的erlang遊戲架構的藕合度仍是挺低的,所以分離出來地圖服務器,用Go從新實現了下,經過socket跟erlang架構部分進行通訊,發現效果異常之好,Go的性能、併發的原生支持再配合上erlang寫遊戲框架,在性能上已經毫不亞於C++框架,可是後者你們都懂,中關村程序員聽說平均壽命50多歲,很大的一部分緣由是由於這個。
之後的路怎麼走?
混合型編程會是之後的主流,由於沒有哪一個語言是完美的,包括被衆多「朝聖者」所推崇的Go,若是咱們能根據本身的業務場景,選對合適的語言,那不敢說事半功10倍,至少事半功倍應該是有的,因此不要被主流語言(Java,C++)禁錮了咱們的世界,侷限了咱們的創新,若是能作到輕鬆愉快的開發,那這個世界該多美好!!