高併發讀與高併發寫的項目總結和mongodb使用中遇到的坑

最近開發了一個既要高併發寫又要高併發讀的項目,寫的QPS比讀還要高,這個需求幾乎是變態的,任何緩存的工具都沒法使用,數據在一秒內可能變化的幾十遍,只能每次請求都實時從數據庫讀取。下面分如下幾點介紹我們是如何技術選型的。


一、選擇通信協議


之前寫的項目併發量很小,http+keepalive完全可以搞定了,此項目http完全架不住,因爲使用http協議傳遞數據,那麼數據最友好的格式就是json,但是親測過json的序列化和反序列化的耗時不可以忍受,並且隨着數據量的增大耗時也將成倍增長,項目的實際應用場景在寫操作時每條數據最大在10KB,因此http+json的組合被kill掉。

只能選擇常見的RPC方式,thrift、protobuf,protobuf需要自己實現tcp的通信方式,我們項目使用的是golang開發,這兩種方式之前都沒有在生產環境使用過,thrift僅僅自己玩過,因此就直接選擇了thrift,編碼的量級也相比protobuf降低好多,不過在代碼寫差不多的時候發現golang有基於protobuf開源的gRpc包。。。

通信協議選擇了thrift,基本確定了C+S的架構模式,Client負責業務端的訪問請求(還是走的http,貌似業務那幫人只會寫業務邏輯,完全不懂架構的東西,如果懂Client都可以幹掉,自己連Server就搞定了),Server負責Client通過thrift協議發來的請求,包括查詢DB和計算。

二、選擇數據庫


數據庫貌似沒得選啊,MySQL肯定是可以的,但是需要對一條完整的業務數據進行拆分到多個表中,這對高併發寫可能會有影響,一次要鎖住多個表的多條數據,對代碼的要求肯定很高,具體沒有嘗試過,只是猜測。MySQL的存儲方案一直都沒有被考慮過。

我們選擇的是mongodb,版本是3.2.11,NOSQL數據庫,支持嵌套文檔,所有的寫操作都可以通過upsert搞定,將併發寫的代碼要求交給mongo去完成,這樣大大降低了編碼的難度,mongo也屬於內存型數據庫吧,數據的讀寫速度還算可以(但是讀的速度還是達不到我們項目的要求。。)。

三、架構部署


架構圖就不給了吧,通過上面的敘述,有經驗的一眼就能想象出來。

我們使用了24臺物理機,每臺物理機一虛21個docker,1Client+10Server+10Mongo,每三個Server+Mongo作爲一個服務節點,因此有24個Client,前面使用Nginx做反向代理處理業務的請求,80個服務節點用於處理數據查詢和計算,最主要的是打平業務的數據,每個服務節點的三個mongo作爲一個副本集(數據完全是一直的),每個client會隨機選擇服務節點裏的某個Server來處理一次請求。

代碼發佈是個老大難問題!!!!

240個Server、240個mongo、24個Client,每個Server都需要通過環境變量來知道自己該連哪個mongo(docker採用的host模式,通過不同端口來控制同一臺物理機的mongo)。運維的同事看到這個項目的發佈需求,直接就懵逼了,因爲之前從來沒有這麼大批量的docker發佈需求。整個項目調動運維+DBA+我們自己+業務等各個部門的人員,而且我們還進行了內核參數優化,機器都重啓了很多遍。

不過運維的同事的某個發佈系統還是幫我們扛起了發佈的重擔,雖然發佈的時間需要很長,但是咱要求的已經不能再多了,畢竟基礎設施還是不夠完善,發佈的大事搞定了,也能在頁面上監控發佈過程中是否報錯,機器各項參數指標的監控等。

四、說說坑吧

變態的項目肯定很多坑,踩了很多,都不知道從哪裏說起,有些是溝通的不徹底,有的是完全沒有遇到過,有的真的沒法徹底解決。

坑1:
上面說過,Server是通過環境變量來確定該連哪個mongo,這不,運維在發佈時,配錯了,頓時就傻逼了,環境變量都人肉檢測了幾遍。

坑2:
業務需求溝通不徹底,業務的計算算法沒說清楚,導致計算結果不對,每次請求必須實時讀數據庫,數據庫內嵌文檔結構的改變,爲此,單機測試了很多遍,代碼也改了很多遍,因爲之前的功能測試,完全不能模擬高併發的場景,一旦高併發,很多問題就暴露出來了。每次修改後發佈都是個漫長的等待過程。。。

坑3:
mongo會自動發現副本集的所有機器地址,之前我們是不知道的,在發現某臺mongo被手動關閉了,居然連接的Server沒有報錯,臥槽,在這裏也折騰了比較長的時間,mgo的文檔讀了一遍又一遍,最後發現確實沒有問題,這樣反而比較好,因爲一旦改成斷掉就報錯,那麼性能反而有下降。

坑4:
這是個巨坑,一直沒有填完,而且好像也不能徹底填完,花的時間也最多,做的各種嘗試也最多。
swap,swap,swap,mongo在內存的available空間還很多的情況下,居然他媽的去使用swap空間,坑啊,此種項目的場景最好全部使用內存才最好,修改了內核參數也沒能徹底解決,最終還是會使用幾十M的swap,而且還會增長。這個後面可能是個隱患。。。


就說這麼多吧,近三個月的時間,第一次寫高併發讀寫的項目,收穫還是很多的,對代碼的要求也很高,挺鍛鍊人的,並且全程的所有代碼都是我來把控的,整個完整的架構也是我來設計的,基本上還算可以吧,雖然纔剛剛接入數據還沒有完全上線。

安利一個自己實現的thrift客戶端連接池golang的開源代碼:https://github.com/xkeyideal/ThriftPool,歡迎使用,在該項目中實踐了沒有任何問題,用的時候希望加上,如果從pool裏拿到的client連接在某次請求出錯時,希望不要再放回池子裏,直接關閉該連接丟棄的代碼,還是很簡單的。


後記


今天終於把坑4給填了,消滅了一個巨大的定時炸彈,目前mongo的swap用量都是0。 
前天發現服務器上某個程序突然崩掉,使用dmesg命令查看,發現文件系統不兼容,宿主機是xfs,docker裏使用的是ext4,文件落盤肯定會出現問題。發現問題後,在保障服務不中斷的前提下,花了兩天時間重裝全部的物理機OS,重裝後已經跑了一天多,尚未發現任何程序使用swap空間的痕跡。這次可謂解決了一個巨大的bug。此前一直懷疑這批機器有問題,果不其然。


====================================2017-09-11 更新======================================

mongodb深入運用

運維的發佈系統只能串行發佈,而我們線上運行了幾百臺docker,每次發佈簡直是令人髮指,全程需要2個小時,同時docker容器裏運行mongodb副本集 dba也不給維護,不過好像mongodb也沒有死過,想看監控信息只能手動登錄到docker裏面敲命令行。

一週之前業務需要擴容機器,機器數量直接翻一倍,如果還照之前那樣部署和運維,那隻要發佈一次,一天就基本上別幹事情了,而且之前我們的mongodb是裸跑的,沒有任何賬號密碼,也就是說,只要任何一個人有線上機器的權限就能登錄我們的數據庫,雖然是公司內部系統,但還是比較危險。

基於上面的各項需求矛盾點,特此花了兩個月時間,開發了一套自動化並行發佈系統,並且只需要點點鼠標就能看到mongodb的一些監控信息,同時mongodb副本集的發佈也告別了運維的腳本,全部實現自動化,發佈480個mongodb --> 3個mongodb組成副本集 --> 爲每個副本集添加admin賬號和普通賬號 --> 各項mongodb監控命令的可視化結果呈現, 可以說是非常的完備,並且可以完全避免之前的文件系統出錯,和numa沒有強行關閉的問題,這些都是可以通過mongodb的指令查詢的。


該系統的mongodb自動化發佈和運維,翻閱了mongodb的文檔;
mongodb的命令行工具參照了[mongo-tools](https://github.com/mongodb/mongo-tools)代碼,僅僅是參考,真正的實現完全重寫了,並且`mongotop`和`mongostat`兩個命令還使用websocket實現實時的輸出。

server和client的發佈,還是直接調用了運維發佈系統的api,只不過按照我們的並行發佈策略,一次性發送多個發佈任務,來達到快速發佈的目的。

雖然屬於特供項目,但是收益還是十分巨大的:
1、擴容後總計48臺物理機 48clients+480servers+480mongodb 從0開始到整個業務部署上線,僅僅耗時1小時不到,如果按照之前的速度,一天時間都搞不定,不需要運維人肉操作一些命令和腳本,所有全部自動化一鍵觸發,實在太爽。
2、對mongodb的理解也進一步深入,該項目有一半的時間,在搞定mongodb從0到所有的自動化發佈和監控上,基本上對mongo副本集的知識有了重新的理解
3、有了可視化命令輸出就是好,爲此發現了go-mgo/mgo驅動的一個小問題


go-mgo/mgo的問題

由於我們的server和mongo一一對應部署在同一臺物理機上,mongodb的讀連接的模式採用了Nearest,但是無意中發現golang的mgo驅動Nearest的實現並沒有按照mongodb文檔(3.2.11版本,我們線上使用的該版本數據庫)選用延遲最低的。導致在網絡十分好的情況下,副本集中的第三臺機器沒有查詢的量。

具體的issue詳見[mgo nearest issue](https://github.com/go-mgo/mgo/issues/487)。

通過查閱mgo的源碼最終發現了問題,具體的分析已經寫在上面的issue裏了,有興趣的可以看看。

解決方案是有的,只需要改掉相應代碼,但個人能力有限,改完之後不能進行完整的測試,我們業務的查詢量比較小,所以就此放過。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章