mongodb线程池_常用高并发网络线程模型设计及MongoDB线程模型优化实践
服務(wù)端通常需要支持高并發(fā)業(yè)務(wù)訪問(wèn),如何設(shè)計(jì)優(yōu)秀的服務(wù)端網(wǎng)絡(luò)IO工作線程/進(jìn)程模型對(duì)業(yè)務(wù)的高并發(fā)訪問(wèn)需求起著至關(guān)重要的核心作用。
本文總結(jié)了了不同場(chǎng)景下的多種網(wǎng)絡(luò)IO線程/進(jìn)程模型,并給出了各種模型的優(yōu)缺點(diǎn)及其性能優(yōu)化方法,非常適合服務(wù)端開(kāi)發(fā)、中間件開(kāi)發(fā)、數(shù)據(jù)庫(kù)開(kāi)發(fā)等開(kāi)發(fā)人員借鑒。
1. 線程模型一:單線程網(wǎng)絡(luò)IO復(fù)用模型
說(shuō)明:
所有網(wǎng)絡(luò)IO事件(accept事件、讀事件、寫(xiě)事件)注冊(cè)到epoll事件集
主循環(huán)中通過(guò)epoll_wait一次性獲取內(nèi)核態(tài)收集到的epoll事件信息,然后輪詢執(zhí)行各個(gè)事件對(duì)應(yīng)的回調(diào)
事件注冊(cè)、epoll_wait事件獲取、事件回調(diào)執(zhí)行全部由一個(gè)線程處理
1.1一個(gè)完整請(qǐng)求組成
一個(gè)完整的請(qǐng)求處理過(guò)程主要包含以下幾個(gè)部分:
步驟1:通過(guò)epoll_wait一次性獲取網(wǎng)絡(luò)IO事件
步驟2:讀取數(shù)據(jù)及協(xié)議解析
步驟3:解析成功后進(jìn)行業(yè)務(wù)邏輯處理,然后應(yīng)答客戶端
1.2 該網(wǎng)絡(luò)線程模型缺陷
所有工作都由一個(gè)線程執(zhí)行,包括epoll事件獲取、事件處理(數(shù)據(jù)讀寫(xiě))、只要任一一個(gè)請(qǐng)求的事件回調(diào)處理阻塞,其他請(qǐng)求都會(huì)阻塞。例如redis的hash結(jié)構(gòu),如果filed過(guò)多,假設(shè)一個(gè)hash key包含數(shù)百萬(wàn)filed,則該Hash key過(guò)期的時(shí)候,整個(gè)redis阻塞。
單線程工作模型,CPU會(huì)成為瓶頸,如果QPS過(guò)高,整個(gè)CPU負(fù)載會(huì)達(dá)到100%,時(shí)延抖動(dòng)厲害。
1.3 典型案例
redis緩存
推特緩存中間件twemproxy
1.4 主循環(huán)工作流程
while (1) { //epoll_wait等待網(wǎng)絡(luò)事件,如果有網(wǎng)絡(luò)事件則返回,或者超時(shí)范圍 size_t numevents= epoll_wait(); //遍歷前面epoll獲取到的網(wǎng)絡(luò)事件,執(zhí)行對(duì)應(yīng)事件回調(diào) for (j = 0; j < numevents; j++) { if(讀事件) { //讀數(shù)據(jù) readData()//解析parseData() //讀事件處理、讀到數(shù)據(jù)后的業(yè)務(wù)邏輯處理 requestDeal() } else if(寫(xiě)事件) { //寫(xiě)事件處理,寫(xiě)數(shù)據(jù)邏輯處理 writeEentDeal() } else {//異常事件處理errorDeal() } }}說(shuō)明:后續(xù)多線程/進(jìn)程模型中,每個(gè)線程/進(jìn)程的主流程和該while()流程一致。
1.5 redis源碼分析及異步網(wǎng)絡(luò)IO復(fù)用精簡(jiǎn)版demo
由于之前工作需要,需要對(duì)redis內(nèi)核做二次優(yōu)化開(kāi)發(fā),因此對(duì)整個(gè)redis代碼做了部分代碼注釋,同時(shí)把redis的網(wǎng)絡(luò)模塊獨(dú)立出來(lái)做成了簡(jiǎn)單demo,該demo對(duì)理解epoll網(wǎng)絡(luò)事件處理及Io復(fù)用實(shí)現(xiàn)會(huì)有幫助,代碼比較簡(jiǎn)短,可以參考如下地址:
《redis源碼詳細(xì)注釋分析》:https://github.com/y123456yz/Reading-and-comprehense-redis-cluster
《redis網(wǎng)絡(luò)模塊精簡(jiǎn)版demo》:https://github.com/y123456yz/middleware_development_learning/tree/master/第一階段-手把手教你做分布式緩存二次開(kāi)發(fā)、性能優(yōu)化/異步網(wǎng)絡(luò)框架零基礎(chǔ)學(xué)習(xí)%2B客戶端結(jié)構(gòu)組織%2B網(wǎng)絡(luò)協(xié)議解析等/asyn_network%2BclientManager%2BprotocolParse
《推特緩存中間件twemproxy源碼分析實(shí)現(xiàn)》:https://github.com/y123456yz/Reading-and-comprehense-twemproxy0.4.1
2. 線程模型二:單listener+固定worker線程
該線程模型圖如下圖所示:
說(shuō)明:
listener線程負(fù)責(zé)接受所有的客戶端鏈接
listener線程每接收到一個(gè)新的客戶端鏈接產(chǎn)生一個(gè)新的fd,然后通過(guò)分發(fā)器發(fā)送給對(duì)應(yīng)的工作線程(hash方式)
工作線程獲取到對(duì)應(yīng)的新鏈接fd后,后續(xù)該鏈接上的所有網(wǎng)絡(luò)IO讀寫(xiě)都由該線程處理
假設(shè)有32個(gè)鏈接,則32個(gè)鏈接建立成功后,每個(gè)線程平均處理4個(gè)鏈接上的讀寫(xiě)、報(bào)文處理、業(yè)務(wù)邏輯處理
2.1 該網(wǎng)絡(luò)線程模型缺陷
進(jìn)行accept處理的listener線程只有一個(gè),在瞬間高并發(fā)場(chǎng)景容易成為瓶頸
一個(gè)線程通過(guò)IO復(fù)用方式處理多個(gè)鏈接fd的數(shù)據(jù)讀寫(xiě)、報(bào)文解析及后續(xù)業(yè)務(wù)邏輯處理,這個(gè)過(guò)程會(huì)有嚴(yán)重的排隊(duì)現(xiàn)象,例如某個(gè)鏈接的報(bào)文接收解析完畢后的內(nèi)部處理時(shí)間過(guò)長(zhǎng),則其他鏈接的請(qǐng)求就會(huì)阻塞排隊(duì)
2.2 典型案例
memcache緩存,適用于內(nèi)部處理比較快的緩存場(chǎng)景、代理中間場(chǎng)景。
memcache源碼實(shí)現(xiàn)中文分析可以詳見(jiàn):
https://github.com/y123456yz/Reading-and-comprehense-memcached-1.4.22
3. 線程模型三:固定worker線程模型(reuseport)
該模型原型圖如下:
說(shuō)明:
Linux kernel 3.9開(kāi)始支持reuseport功能,內(nèi)核協(xié)議棧每獲取到一個(gè)新鏈接自動(dòng)均衡分發(fā)給用戶態(tài)worker線程。
該模型解決了模型一的listener單點(diǎn)瓶頸問(wèn)題,多個(gè)進(jìn)程/線程同時(shí)做為listener,都可以accept客戶端新鏈接。
3.1 該網(wǎng)絡(luò)進(jìn)程/線程模型缺陷
reuseport支持后,內(nèi)核通過(guò)負(fù)載均衡的方式分發(fā)不同新鏈接到多個(gè)用戶態(tài)worker進(jìn)程/線程,每個(gè)進(jìn)程/線程通過(guò)IO復(fù)用方式處理多個(gè)客戶端新鏈接fd的數(shù)據(jù)讀寫(xiě)、報(bào)文解析、解析后的業(yè)務(wù)邏輯處理。每個(gè)工作進(jìn)程/線程同時(shí)處理多個(gè)鏈接的請(qǐng)求,如果某個(gè)鏈接的報(bào)文接收解析完畢后的內(nèi)部處理時(shí)間過(guò)長(zhǎng),則其他鏈接的請(qǐng)求就會(huì)阻塞排隊(duì)。
該模型雖然解決了listener單點(diǎn)瓶頸問(wèn)題,但是工作線程內(nèi)部的排隊(duì)問(wèn)題沒(méi)有解決。
不過(guò),Nginx作為七層轉(zhuǎn)發(fā)代理,由于都是內(nèi)存處理,所以內(nèi)部處理時(shí)間比較短,所以適用于該模型。
3.2典型案例
nginx (nginx用的是進(jìn)程,模型原理一樣),該模型適用于內(nèi)部業(yè)務(wù)邏輯簡(jiǎn)單的場(chǎng)景,如nginx代理等。
reuseport支持性能提升過(guò)程可以參考另一篇分享:《Nginx多進(jìn)程高并發(fā)、低時(shí)延、高可靠機(jī)制在緩存(redis、memcache)twemproxy代理中的應(yīng)用》(https://my.oschina.net/u/4087916/blog/3016162)
另,參考《nginx源碼中文注釋分析》(https://github.com/y123456yz/reading-code-of-nginx-1.9.2)
4. 線程模型四:一個(gè)鏈接一個(gè)線程模型
該線程模型圖如下圖:
說(shuō)明:
listener線程負(fù)責(zé)接受所有的客戶端鏈接。
listener線程每接收到一個(gè)新的客戶端鏈接就創(chuàng)建一個(gè)線程,該線程只負(fù)責(zé)處理該鏈接上的數(shù)據(jù)讀寫(xiě)、報(bào)文解析、業(yè)務(wù)邏輯處理。
4.1 該網(wǎng)絡(luò)線程模型缺陷
一個(gè)鏈接創(chuàng)建一個(gè)線程,如果10萬(wàn)個(gè)鏈接,那么就需要10萬(wàn)個(gè)線程,線程數(shù)太多,系統(tǒng)負(fù)責(zé)、內(nèi)存消耗也會(huì)很多
當(dāng)鏈接關(guān)閉的時(shí)候,線程也需要銷(xiāo)毀,頻繁的線程創(chuàng)建和消耗進(jìn)一步增加系統(tǒng)負(fù)載
4.2 典型案例
mysql默認(rèn)方式、MongoDB同步線程模型配置,適用于請(qǐng)求處理比較耗時(shí)的場(chǎng)景,如數(shù)據(jù)庫(kù)服務(wù)
Apache web服務(wù)器,該模型限制了apache性能,nginx優(yōu)勢(shì)會(huì)更加明顯
5. 線程模型五:單listener+動(dòng)態(tài)worker線程(單隊(duì)列)
該線程模型圖如下圖所示:
說(shuō)明:
listener線程接收到一個(gè)新鏈接fd后,把該fd交由線程池處理,后續(xù)該鏈接的所有讀寫(xiě)、報(bào)文解析、業(yè)務(wù)處理都由線程池中多個(gè)線程處理。
該模型把一次請(qǐng)求轉(zhuǎn)換為多個(gè)任務(wù)(網(wǎng)絡(luò)數(shù)據(jù)讀寫(xiě)、報(bào)文解析、報(bào)文解析后的業(yè)務(wù)邏輯處理)入隊(duì)到全局隊(duì)列,線程池中的線程從隊(duì)列中獲取任務(wù)執(zhí)行。
同一個(gè)請(qǐng)求訪問(wèn)被拆分為多個(gè)任務(wù),一次請(qǐng)求可能由多個(gè)線程處理。
當(dāng)任務(wù)太多,系統(tǒng)壓力大的時(shí)候,線程池中線程數(shù)動(dòng)態(tài)增加。
當(dāng)任務(wù)減少,系統(tǒng)壓力減少的時(shí)候,線程池中線程數(shù)動(dòng)態(tài)減少。
5.1 工作線程運(yùn)行時(shí)間相關(guān)的幾個(gè)統(tǒng)計(jì)
T1: 調(diào)用底層asio庫(kù)接收一個(gè)完整MongoDB報(bào)文的時(shí)間
T2: 接收到報(bào)文后的后續(xù)所有處理(含報(bào)文解析、認(rèn)證、引擎層處理、發(fā)送數(shù)據(jù)給客戶端等)
T3: 線程等待數(shù)據(jù)的時(shí)間(例如:長(zhǎng)時(shí)間沒(méi)有流量,則現(xiàn)在等待讀取數(shù)據(jù))
5.2單個(gè)工作線程如何判斷自己處于”空閑”狀態(tài)
線程運(yùn)行總時(shí)間=T1 + T2 +T3,其中T3是無(wú)用等待時(shí)間。如果T3的無(wú)用等待時(shí)間占比很大,則說(shuō)明線程比較空閑。工作線程每一次循環(huán)處理后判斷有效時(shí)間占比,如果小于指定閥值,則自己直接exit退出銷(xiāo)毀
5.3 如何判斷線程池中工作線程“太忙”
控制線程專門(mén)用于判斷線程池中工作線程的壓力情況,以此來(lái)決定是否在線程池中創(chuàng)建新的工作線程來(lái)提升性能。
控制線程每過(guò)一定時(shí)間循環(huán)檢查線程池中的線程壓力狀態(tài),實(shí)現(xiàn)原理就是簡(jiǎn)單的實(shí)時(shí)記錄線程池中的線程當(dāng)前運(yùn)行情況,為以下兩類(lèi)計(jì)數(shù):總線程數(shù)_threadsRunning、當(dāng)前正在運(yùn)行task任務(wù)的線程數(shù)_threadsInUse。如果_threadsRunning=_threadsRunning,說(shuō)明所有工作線程當(dāng)前都在處理task任務(wù),線程池中線程壓力大,這時(shí)候控制線程就開(kāi)始增加線程池中線程數(shù)。
該模型詳細(xì)源碼實(shí)現(xiàn)過(guò)程更多細(xì)節(jié)我們之前發(fā)布的文章:《MongoDB網(wǎng)絡(luò)傳輸處理源碼實(shí)現(xiàn)及性能調(diào)優(yōu)——體驗(yàn)內(nèi)核性能極致設(shè)計(jì)》
5.4 該網(wǎng)絡(luò)線程模型缺陷
線程池獲取任務(wù)執(zhí)行,有鎖競(jìng)爭(zhēng),這里就會(huì)成為系統(tǒng)瓶頸
5.5 典型案例
MongoDB動(dòng)態(tài)adaptive線程模型,適用于請(qǐng)求處理比較耗時(shí)的場(chǎng)景,如數(shù)據(jù)庫(kù)服務(wù)
該模型詳細(xì)源碼優(yōu)化分析實(shí)現(xiàn)過(guò)程同樣參考:
《Mongodb網(wǎng)絡(luò)傳輸處理源碼實(shí)現(xiàn)及性能調(diào)優(yōu)——體驗(yàn)內(nèi)核性能極致設(shè)計(jì)》
6. 線程模型六:單listener+動(dòng)態(tài)worker線程(多隊(duì)列)-MongoDB網(wǎng)絡(luò)線程模型優(yōu)化實(shí)踐
該線程模型圖如下:
說(shuō)明:
把一個(gè)全局隊(duì)列拆分為多個(gè)隊(duì)列,任務(wù)入隊(duì)的時(shí)候按照hash散列到各自的隊(duì)列,工作線程獲取獲取任務(wù)的時(shí)候,同理通過(guò)hash的方式去對(duì)應(yīng)的隊(duì)列獲取任務(wù),通過(guò)這種方式減少鎖競(jìng)爭(zhēng),同時(shí)提升整體性能。
6.1典型案例
OPPO自研MongoDB內(nèi)核多隊(duì)列adaptive線程模型優(yōu)化,性能有很好的提升,適用于請(qǐng)求處理比較耗時(shí)的場(chǎng)景,如數(shù)據(jù)庫(kù)服務(wù)。該模型詳細(xì)源碼優(yōu)化分析實(shí)現(xiàn)過(guò)程再次參考:《Mongodb網(wǎng)絡(luò)傳輸處理源碼實(shí)現(xiàn)及性能調(diào)優(yōu)——體驗(yàn)內(nèi)核性能極致設(shè)計(jì)》
6.2 疑問(wèn)?為什么MySQL、MongoDB等數(shù)據(jù)庫(kù)沒(méi)有利用內(nèi)核的reuseport特殊-多線程同時(shí)處理accept請(qǐng)求?
答:實(shí)際上所有服務(wù)都可以利用這一特性,包括數(shù)據(jù)庫(kù)服務(wù)(MongoDB、mysql等)。但是因?yàn)閿?shù)據(jù)庫(kù)服務(wù)訪問(wèn)時(shí)延一般都是ms級(jí)別,如果reuseport特性利用起來(lái),時(shí)延會(huì)有幾十us的性能提升,這相比數(shù)據(jù)庫(kù)內(nèi)部處理的ms級(jí)時(shí)延,這幾十us的性能提升,基本上可以忽略掉,這也是大部分?jǐn)?shù)據(jù)庫(kù)服務(wù)沒(méi)有支持該功能的原因。
緩存,代理等中間件,由于本身內(nèi)部處理時(shí)間就比較小,也是us級(jí)別,所以需要充分利用該特性。
☆?END?☆
招聘信息OPPO互聯(lián)網(wǎng)云平臺(tái)團(tuán)隊(duì)招聘一大波崗位,涵蓋Java、容器、Linux內(nèi)核開(kāi)發(fā)、產(chǎn)品經(jīng)理、項(xiàng)目經(jīng)理等多個(gè)方向,請(qǐng)?jiān)诠娞?hào)后臺(tái)回復(fù)關(guān)鍵詞“云招聘”查看查詳細(xì)信息。
你可能還喜歡百萬(wàn)級(jí)高并發(fā)MongoDB集群性能數(shù)十倍提升優(yōu)化實(shí)踐(上)
OPPO百萬(wàn)級(jí)高并發(fā)MongoDB集群性能數(shù)十倍提升優(yōu)化實(shí)踐(下)
MongoDB網(wǎng)絡(luò)傳輸處理源碼實(shí)現(xiàn)及性能調(diào)優(yōu)——體驗(yàn)內(nèi)核性能極致設(shè)計(jì)
更多技術(shù)干貨
掃碼關(guān)注
OPPO互聯(lián)網(wǎng)技術(shù)
?我就知道你“在看”總結(jié)
以上是生活随笔為你收集整理的mongodb线程池_常用高并发网络线程模型设计及MongoDB线程模型优化实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python语言入门r_小结:jieba
- 下一篇: mysql 排序取前4_MySQL时间段