深入理解IO復(fù)用并發(fā)模型
本文主要介紹常見的Server的并發(fā)模型,這些模型與編程語言本身無關(guān),有的編程語言可能在語法上直接透明了模型本質(zhì),所以開發(fā)者沒必要一定要基于模型去編寫,只是需要知道和了解并發(fā)模型的構(gòu)成和特點(diǎn)即可。
那么在了解并發(fā)模型之前,我們需要兩個必備的前置知識:
socket網(wǎng)絡(luò)編程
多路IO復(fù)用機(jī)制
多線程/多進(jìn)程等并發(fā)編程理論
模型一、單線程Accept(無IO復(fù)用)
① 主線程main thread執(zhí)行阻塞Accept,每次客戶端Connect鏈接過來,main thread中accept響應(yīng)并建立連接
② 創(chuàng)建鏈接成功,得到Connfd1套接字后, 依然在main thread串行處理套接字讀寫,并處理業(yè)務(wù)。
③ 在②處理業(yè)務(wù)中,如果有新客戶端Connect過來,Server無響應(yīng),直到當(dāng)前套接字全部業(yè)務(wù)處理完畢。
④ 當(dāng)前客戶端處理完后,完畢鏈接,處理下一個客戶端請求。
優(yōu)點(diǎn):
socket編程流程清晰且簡單,適合學(xué)習(xí)使用,了解socket基本編程流程。
缺點(diǎn):
該模型并非并發(fā)模型,是串行的服務(wù)器,同一時(shí)刻,監(jiān)聽并響應(yīng)最大的網(wǎng)絡(luò)請求量為1。 即并發(fā)量為1。
僅適合學(xué)習(xí)基本socket編程,不適合任何服務(wù)器Server構(gòu)建。
模型二、單線程Accept+多線程讀寫業(yè)務(wù)(無IO復(fù)用)
① 主線程main thread執(zhí)行阻塞Accept,每次客戶端Connect鏈接過來,main thread中accept響應(yīng)并建立連接
② 創(chuàng)建鏈接成功,得到Connfd1套接字后,創(chuàng)建一個新線程thread1用來處理客戶端的讀寫業(yè)務(wù)。main thead依然回到Accept阻塞等待新客戶端。
③ thread1通過套接字Connfd1與客戶端進(jìn)行通信讀寫。
④ server在②處理業(yè)務(wù)中,如果有新客戶端Connect過來,main thread中Accept依然響應(yīng)并建立連接,重復(fù)②過程。
優(yōu)點(diǎn):
基于模型一:單線程Accept(無IO復(fù)用) 支持了并發(fā)的特性。
使用靈活,一個客戶端對應(yīng)一個線程單獨(dú)處理,server處理業(yè)務(wù)內(nèi)聚程度高,客戶端無論如何寫,服務(wù)端均會有一個線程做資源響應(yīng)。
缺點(diǎn):
隨著客戶端的數(shù)量增多,需要開辟的線程也增加,客戶端與server線程數(shù)量1:1正比關(guān)系,一次對于高并發(fā)場景,線程數(shù)量收到硬件上限瓶頸。
對于長鏈接,客戶端一旦無業(yè)務(wù)讀寫,只要不關(guān)閉,server的對應(yīng)線程依然需要保持連接(心跳、健康監(jiān)測等機(jī)制),占用連接資源和線程開銷資源浪費(fèi)。
僅適合客戶端數(shù)量不大,并且數(shù)量可控的場景使用。
僅適合學(xué)習(xí)基本socket編程,不適合任何服務(wù)器Server構(gòu)建。
模型三、單線程多路IO復(fù)用
① 主線程main thread創(chuàng)建listenFd之后,采用多路I/O復(fù)用機(jī)制(如:select、epoll)進(jìn)行IO狀態(tài)阻塞監(jiān)控。有Client1客戶端Connect請求,I/O復(fù)用機(jī)制檢測到ListenFd觸發(fā)讀事件,則進(jìn)行Accept建立連接,并將新生成的connFd1加入到監(jiān)聽I/O集合中。
② Client1再次進(jìn)行正常讀寫業(yè)務(wù)請求,main thread的多路I/O復(fù)用機(jī)制阻塞返回,會觸該套接字的讀/寫事件等。
③ 對于Client1的讀寫業(yè)務(wù),Server依然在main thread執(zhí)行流程提繼續(xù)執(zhí)行,此時(shí)如果有新的客戶端Connect鏈接請求過來,Server將沒有即時(shí)響應(yīng)。
④ 等到Server處理完一個連接的Read+Write操作,繼續(xù)回到多路I/O復(fù)用機(jī)制阻塞,其他鏈接過來重復(fù) ②、③流程。
優(yōu)點(diǎn):
單流程解決了可以同時(shí)監(jiān)聽多個客戶端讀寫狀態(tài)的模型,不需要1:1與客戶端的線程數(shù)量關(guān)系。
多路I/O復(fù)用阻塞,非忙詢狀態(tài),不浪費(fèi)CPU資源, CPU利用率較高。
缺點(diǎn):
雖然可以監(jiān)聽多個客戶端的讀寫狀態(tài),但是同一時(shí)間內(nèi),只能處理一個客戶端的讀寫操作,實(shí)際上讀寫的業(yè)務(wù)并發(fā)為1。
多客戶端訪問Server,業(yè)務(wù)為串行執(zhí)行,大量請求會有排隊(duì)延遲現(xiàn)象,如圖中⑤所示,當(dāng)Client3占據(jù)main thread流程時(shí),Client1,Client2流程卡在IO復(fù)用等待下次監(jiān)聽觸發(fā)事件。
模型四、單線程多路IO復(fù)用+多線程讀寫業(yè)務(wù)(業(yè)務(wù)工作池)
① 主線程main thread創(chuàng)建listenFd之后,采用多路I/O復(fù)用機(jī)制(如:select、epoll)進(jìn)行IO狀態(tài)阻塞監(jiān)控。有Client1客戶端Connect請求,I/O復(fù)用機(jī)制檢測到ListenFd觸發(fā)讀事件,則進(jìn)行Accept建立連接,并將新生成的connFd1加入到監(jiān)聽I/O集合中。
② 當(dāng)connFd1有可讀消息,觸發(fā)讀事件,并且進(jìn)行讀寫消息
③ main thread按照固定的協(xié)議讀取消息,并且交給worker pool工作線程池, 工作線程池在server啟動之前就已經(jīng)開啟固定數(shù)量的thread,里面的線程只處理消息業(yè)務(wù),不進(jìn)行套接字讀寫操作。
④ 工作池處理完業(yè)務(wù),觸發(fā)connFd1寫事件,將回執(zhí)客戶端的消息通過main thead寫給對方。
優(yōu)點(diǎn):
對于模型三, 將業(yè)務(wù)處理部分,通過工作池分離出來,減少多客戶端訪問Server,業(yè)務(wù)為串行執(zhí)行,大量請求會有排隊(duì)延遲時(shí)間。
實(shí)際上讀寫的業(yè)務(wù)并發(fā)為1,但是業(yè)務(wù)流程并發(fā)為worker pool線程數(shù)量,加快了業(yè)務(wù)處理并行效率。
缺點(diǎn):
讀寫依然為main thread單獨(dú)處理,最高讀寫并行通道依然為1.
雖然多個worker線程處理業(yè)務(wù),但是最后返回給客戶端,依舊需要排隊(duì),因?yàn)槌隹谶€是main thread的Read + Write
模型五、單線程IO復(fù)用+多線程IO復(fù)用(鏈接線程池)
① Server在啟動監(jiān)聽之前,開辟固定數(shù)量(N)的線程,用Thead Pool線程池管理
② 主線程main thread創(chuàng)建listenFd之后,采用多路I/O復(fù)用機(jī)制(如:select、epoll)進(jìn)行IO狀態(tài)阻塞監(jiān)控。有Client1客戶端Connect請求,I/O復(fù)用機(jī)制檢測到ListenFd觸發(fā)讀事件,則進(jìn)行Accept建立連接,并將新生成的connFd1分發(fā)給Thread Pool中的某個線程進(jìn)行監(jiān)聽。
③ Thread Pool中的每個thread都啟動多路I/O復(fù)用機(jī)制(select、epoll),用來監(jiān)聽main thread建立成功并且分發(fā)下來的socket套接字。
④ 如圖, thread監(jiān)聽ConnFd1、ConnFd2, thread2監(jiān)聽ConnFd3,thread3監(jiān)聽ConnFd4. 當(dāng)對應(yīng)的ConnFd有讀寫事件,對應(yīng)的線程處理該套接字的讀寫及業(yè)務(wù)。
優(yōu)點(diǎn):
將main thread的單流程讀寫,分散到多線程完成,這樣增加了同一時(shí)刻的讀寫并行通道,并行通道數(shù)量N, N為線程池Thread數(shù)量。
server同時(shí)監(jiān)聽的ConnFd套接字?jǐn)?shù)量幾乎成倍增大,之前的全部監(jiān)控?cái)?shù)量取決于main thread的多路I/O復(fù)用機(jī)制的最大限制***(select 默認(rèn)為1024, epoll默認(rèn)與內(nèi)存大小相關(guān),約3~6w不等)***,所以理論單點(diǎn)Server最高響應(yīng)并發(fā)數(shù)量為N*(3~6W)(N為線程池Thread數(shù)量,建議與CPU核心成比例1:1)。
如果良好的線程池?cái)?shù)量和CPU核心數(shù)適配,那么可以嘗試CPU核心與Thread進(jìn)行綁定,從而降低CPU的切換頻率,提升每個Thread處理合理業(yè)務(wù)的效率,降低CPU切換成本開銷。
缺點(diǎn):
雖然監(jiān)聽的并發(fā)數(shù)量提升,但是最高讀寫并行通道依然為N,而且多個身處同一個Thread的客戶端,會出現(xiàn)讀寫延遲現(xiàn)象,實(shí)際上每個Thread的模型特征與模型三:單線程多路IO復(fù)用一致。
模型五(進(jìn)程版)、單進(jìn)程多路I/O復(fù)用+多進(jìn)程多路I/O復(fù)用(進(jìn)程池)
與五、單線程IO復(fù)用+多線程IO復(fù)用(鏈接線程池)無大差異。
不同處
進(jìn)程和線程的內(nèi)存布局不同導(dǎo)致,main process(主進(jìn)程)不再進(jìn)行Accept操作,而是將Accept過程分散到各個子進(jìn)程(process)中.
進(jìn)程的特性,資源獨(dú)立,所以main process如果Accept成功的fd,其他進(jìn)程無法共享資源,所以需要各子進(jìn)程自行Accept創(chuàng)建鏈接
main process只是監(jiān)聽ListenFd狀態(tài),一旦觸發(fā)讀事件(有新連接請求). 通過一些IPC(進(jìn)程間通信:如信號、共享內(nèi)存、管道)等, 讓各自子進(jìn)程Process競爭Accept完成鏈接建立,并各自監(jiān)聽。
與五、單線程IO復(fù)用+多線程IO復(fù)用(鏈接線程池)無大差異。
不同處:
多進(jìn)程內(nèi)存資源空間占用稍微大一些
多進(jìn)程模型安全穩(wěn)定型較強(qiáng),這也是因?yàn)楦髯赃M(jìn)程互不干擾的特點(diǎn)導(dǎo)致。
模型六、單線程多路I/O復(fù)用+多線程多路I/O復(fù)用+多線程
① Server在啟動監(jiān)聽之前,開辟固定數(shù)量(N)的線程,用Thead Pool線程池管理
② 主線程main thread創(chuàng)建listenFd之后,采用多路I/O復(fù)用機(jī)制(如:select、epoll)進(jìn)行IO狀態(tài)阻塞監(jiān)控。有Client1客戶端Connect請求,I/O復(fù)用機(jī)制檢測到ListenFd觸發(fā)讀事件,則進(jìn)行Accept建立連接,并將新生成的connFd1分發(fā)給Thread Pool中的某個線程進(jìn)行監(jiān)聽。
③ Thread Pool中的每個thread都啟動多路I/O復(fù)用機(jī)制(select、epoll),用來監(jiān)聽main thread建立成功并且分發(fā)下來的socket套接字。一旦其中某個被監(jiān)聽的客戶端套接字觸發(fā)I/O讀寫事件,那么,會立刻開辟一個新線程來處理I/O讀寫業(yè)務(wù)。
④ 但某個讀寫線程完成當(dāng)前讀寫業(yè)務(wù),如果當(dāng)前套接字沒有被關(guān)閉,那么將當(dāng)前客戶端套接字如:ConnFd3重新加回線程池的監(jiān)控線程中,同時(shí)自身線程自我銷毀。
優(yōu)點(diǎn):
在模型五、單線程IO復(fù)用+多線程IO復(fù)用(鏈接線程池)基礎(chǔ)上,除了能夠保證同時(shí)響應(yīng)的最高并發(fā)數(shù),又能解決讀寫并行通道局限的問題。
同一時(shí)刻的讀寫并行通道,達(dá)到最大化極限,一個客戶端可以對應(yīng)一個單獨(dú)執(zhí)行流程處理讀寫業(yè)務(wù),讀寫并行通道與客戶端數(shù)量1:1關(guān)系。
缺點(diǎn):
該模型過于理想化,因?yàn)橐驝PU核心數(shù)量足夠大。
如果硬件CPU數(shù)量可數(shù)(目前的硬件情況),那么該模型將造成大量的CPU切換成本浪費(fèi)。因?yàn)闉榱吮WC讀寫并行通道與客戶端1:1的關(guān)系,那么Server需要開辟的Thread數(shù)量就與客戶端一致,那么線程池中做多路I/O復(fù)用的監(jiān)聽線程池綁定CPU數(shù)量將變得毫無意義。
如果每個臨時(shí)的讀寫Thread都能夠綁定一個單獨(dú)的CPU,那么此模型將是最優(yōu)模型。但是目前CPU的數(shù)量無法與客戶端的數(shù)量達(dá)到一個量級,目前甚至差的不是幾個量級的事。
總結(jié)
綜上,我們整理了7中Server的服務(wù)器處理結(jié)構(gòu)模型,每個模型都有各自的特點(diǎn)和優(yōu)勢,那么對于多少應(yīng)付高并發(fā)和高CPU利用率的模型,目前多數(shù)采用的是模型五(或模型五進(jìn)程版,如Nginx就是類似模型五進(jìn)程版的改版)。
至于并發(fā)模型并非設(shè)計(jì)的約復(fù)雜越好,也不是線程開辟的越多越好,我們要考慮硬件的利用與和切換成本的開銷。模型六設(shè)計(jì)就極為復(fù)雜,線程較多,但以當(dāng)今的硬件能力無法支撐,反倒導(dǎo)致該模型性能極差。所以對于不同的業(yè)務(wù)場景也要選擇適合的模型構(gòu)建,并不是一定固定就要使用某個來應(yīng)用。
Go 任務(wù)調(diào)度 多線程
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。