Socket Server的N種并發(fā)模型匯總

原創(chuàng)聲明
作者: 劉丹冰Aceld

本文主要介紹常見的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ù)用)

(1) 模型結(jié)構(gòu)圖
(2) 模型分析

① 主線程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)前客戶端處理完后垂睬,完畢鏈接媳荒,處理下一個客戶端請求。

(3) 優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • socket編程流程清晰且簡單驹饺,適合學(xué)習(xí)使用钳枕,了解socket基本編程流程。

缺點(diǎn)

  • 該模型并非并發(fā)模型赏壹,是串行的服務(wù)器鱼炒,同一時刻,監(jiān)聽并響應(yīng)最大的網(wǎng)絡(luò)請求量為1蝌借。 即并發(fā)量為1昔瞧。

  • 僅適合學(xué)習(xí)基本socket編程,不適合任何服務(wù)器Server構(gòu)建菩佑。


模型二自晰、單線程Accept+多線程讀寫業(yè)務(wù)(無IO復(fù)用)

(1) 模型結(jié)構(gòu)圖
(2) 模型分析

① 主線程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 threadAccept依然響應(yīng)并建立連接遣疯,重復(fù)②過程雄可。

(3) 優(yōu)缺點(diǎn)

優(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ù)用

(1) 模型結(jié)構(gòu)圖
(2) 模型分析

① 主線程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í)行棱诱,此時如果有新的客戶端Connect鏈接請求過來,Server將沒有即時響應(yīng)涝动。

④ 等到Server處理完一個連接的Read+Write操作迈勋,繼續(xù)回到多路I/O復(fù)用機(jī)制阻塞,其他鏈接過來重復(fù) ②醋粟、③流程靡菇。

(3) 優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 單流程解決了可以同時監(jiān)聽多個客戶端讀寫狀態(tài)的模型重归,不需要1:1與客戶端的線程數(shù)量關(guān)系。
  • 多路I/O復(fù)用阻塞厦凤,非忙詢狀態(tài)鼻吮,不浪費(fèi)CPU資源, CPU利用率較高泳唠。

缺點(diǎn)

  • 雖然可以監(jiān)聽多個客戶端的讀寫狀態(tài)狈网,但是同一時間內(nèi),只能處理一個客戶端的讀寫操作笨腥,實際上讀寫的業(yè)務(wù)并發(fā)為1拓哺。
  • 多客戶端訪問Server,業(yè)務(wù)為串行執(zhí)行脖母,大量請求會有排隊延遲現(xiàn)象士鸥,如圖中⑤所示,當(dāng)Client3占據(jù)main thread流程時谆级,Client1,Client2流程卡在IO復(fù)用等待下次監(jiān)聽觸發(fā)事件烤礁。

模型四、單線程多路IO復(fù)用+多線程讀寫業(yè)務(wù)(業(yè)務(wù)工作池)

(1) 模型結(jié)構(gòu)圖
(2) 模型分析

① 主線程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寫給對方哮塞。

(3) 優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 對于模型三, 將業(yè)務(wù)處理部分,通過工作池分離出來斥铺,減少多客戶端訪問Server彻桃,業(yè)務(wù)為串行執(zhí)行坛善,大量請求會有排隊延遲時間晾蜘。
  • 實際上讀寫的業(yè)務(wù)并發(fā)為1邻眷,但是業(yè)務(wù)流程并發(fā)為worker pool線程數(shù)量,加快了業(yè)務(wù)處理并行效率剔交。

缺點(diǎn)

  • 讀寫依然為main thread單獨(dú)處理肆饶,最高讀寫并行通道依然為1.
  • 雖然多個worker線程處理業(yè)務(wù),但是最后返回給客戶端岖常,依舊需要排隊驯镊,因為出口還是main threadRead + Write

模型五、單線程IO復(fù)用+多線程IO復(fù)用(鏈接線程池)

(1) 模型結(jié)構(gòu)圖
(2) 模型分析

① 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ù)。

(3) 優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • main thread的單流程讀寫舔亭,分散到多線程完成些膨,這樣增加了同一時刻的讀寫并行通道,并行通道數(shù)量N钦铺, N為線程池Thread數(shù)量订雾。
  • server同時監(jiān)聽的ConnFd套接字數(shù)量幾乎成倍增大,之前的全部監(jiān)控數(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)噩峦。
  • 如果良好的線程池數(shù)量和CPU核心數(shù)適配,那么可以嘗試CPU核心與Thread進(jìn)行綁定抽兆,從而降低CPU的切換頻率识补,提升每個Thread處理合理業(yè)務(wù)的效率,降低CPU切換成本開銷辫红。

缺點(diǎn)

  • 雖然監(jiān)聽的并發(fā)數(shù)量提升凭涂,但是最高讀寫并行通道依然為N祝辣,而且多個身處同一個Thread的客戶端,會出現(xiàn)讀寫延遲現(xiàn)象切油,實際上每個Thread的模型特征與模型三:單線程多路IO復(fù)用一致蝙斜。

模型五(進(jìn)程版)、單進(jìn)程多路I/O復(fù)用+多進(jìn)程多路I/O復(fù)用(進(jìn)程池)

(1) 模型結(jié)構(gòu)圖
(2) 模型分析

五澎胡、單線程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)聽那槽。
(3) 優(yōu)缺點(diǎn)

五悼沿、單線程IO復(fù)用+多線程IO復(fù)用(鏈接線程池)無大差異。

不同處:

多進(jìn)程內(nèi)存資源空間占用稍微大一些

多進(jìn)程模型安全穩(wěn)定型較強(qiáng)骚灸,這也是因為各自進(jìn)程互不干擾的特點(diǎn)導(dǎo)致糟趾。


模型六、單線程多路I/O復(fù)用+多線程多路I/O復(fù)用+多線程

(1) 模型結(jié)構(gòu)圖
(2) 模型分析

① 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)控線程中,同時自身線程自我銷毀靴寂。

(3) 優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 模型五汉额、單線程IO復(fù)用+多線程IO復(fù)用(鏈接線程池)基礎(chǔ)上,除了能夠保證同時響應(yīng)的最高并發(fā)數(shù)榨汤,又能解決讀寫并行通道局限的問題。

  • 同一時刻的讀寫并行通道怎茫,達(dá)到最大化極限收壕,一個客戶端可以對應(yīng)一個單獨(dú)執(zhí)行流程處理讀寫業(yè)務(wù),讀寫并行通道與客戶端數(shù)量1:1關(guān)系轨蛤。

缺點(diǎn)

  • 該模型過于理想化蜜宪,因為要求CPU核心數(shù)量足夠大。
  • 如果硬件CPU數(shù)量可數(shù)(目前的硬件情況)祥山,那么該模型將造成大量的CPU切換成本浪費(fèi)圃验。因為為了保證讀寫并行通道與客戶端1:1的關(guān)系,那么Server需要開辟的Thread數(shù)量就與客戶端一致缝呕,那么線程池中做多路I/O復(fù)用的監(jiān)聽線程池綁定CPU數(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è)計的約復(fù)雜越好香缺,也不是線程開辟的越多越好手销,我們要考慮硬件的利用與和切換成本的開銷。模型六設(shè)計就極為復(fù)雜图张,線程較多原献,但以當(dāng)今的硬件能力無法支撐,反倒導(dǎo)致該模型性能極差埂淮。所以對于不同的業(yè)務(wù)場景也要選擇適合的模型構(gòu)建姑隅,并不是一定固定就要使用某個來應(yīng)用。


關(guān)于作者:

mail: danbing.at@gmail.com
github: https://github.com/aceld
原創(chuàng)書籍gitbook: https://www.kancloud.cn/@aceld


文章推薦

開源軟件作品

(原創(chuàng)開源)Zinx-基于Golang輕量級服務(wù)器并發(fā)框架-完整版(附教程視頻)

(原創(chuàng)開源)Lars-基于C++負(fù)載均衡遠(yuǎn)程調(diào)度系統(tǒng)-完整版

精選文章

典藏版-Golang調(diào)度器GMP原理與調(diào)度全分析

典藏版-Golang三色標(biāo)記倔撞、混合寫屏障GC模式圖文全分析

最常用的調(diào)試 golang 的 bug 以及性能問題的實踐方法讲仰?

Golang中的Defer必掌握的7知識點(diǎn)

Golang中的局部變量“何時棧?何時堆?”

使用Golang的interface接口設(shè)計原則

流?I/O操作痪蝇?阻塞鄙陡?epoll?

深入淺出Golang的協(xié)程池設(shè)計

Go語言構(gòu)建微服務(wù)一站式解決方案


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末冕房,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子趁矾,更是在濱河造成了極大的恐慌耙册,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毫捣,死亡現(xiàn)場離奇詭異详拙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蔓同,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門饶辙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人斑粱,你說我怎么就攤上這事弃揽。” “怎么了则北?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵矿微,是天一觀的道長。 經(jīng)常有香客問我尚揣,道長冷冗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任惑艇,我火速辦了婚禮蒿辙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滨巴。我一直安慰自己思灌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布恭取。 她就那樣靜靜地躺著泰偿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜈垮。 梳的紋絲不亂的頭發(fā)上耗跛,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音攒发,去河邊找鬼调塌。 笑死,一個胖子當(dāng)著我的面吹牛惠猿,可吹牛的內(nèi)容都是我干的羔砾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼姜凄!你這毒婦竟也來了政溃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤态秧,失蹤者是張志新(化名)和其女友劉穎董虱,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體申鱼,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡愤诱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了润讥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡盘寡,死狀恐怖楚殿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竿痰,我是刑警寧澤脆粥,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站影涉,受9級特大地震影響变隔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蟹倾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一匣缘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鲜棠,春花似錦肌厨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盒音,卻和暖如春表鳍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祥诽。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工譬圣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人雄坪。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓胁镐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盯漂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容