本文主要介紹常見的Server的并發模型,這些模型與程式設計語言本身無關,有的程式設計語言可能在文法上直接透明了模型本質,是以開發者沒必要一定要基于模型去編寫,隻是需要知道和了解并發模型的構成和特點即可。
在了解并發模型之前,我們需要兩個必備的前置知識:
- socket網絡程式設計
- 多路IO複用機制
- 多線程/多程序等并發程式設計理論
提綱
模型一
單線程Accept(無I/O複用)
模型二
單線程Accept+多線程讀寫業務(無I/O複用)
模型三
單線程多路I/O複用
模型四
單線程多路I/O複用+多線程讀寫業務(業務工作池)
模型五
單線程多路I/O複用+多線程多路I/O複用(線程池)
模型五(程序版)
單程序多路I/O複用+多程序多路I/O複用(程序池)
模型六
單線程多路I/O複用+多線程多路I/O複用+多線程
模型一、單線程Accept(無I/O複用)
01
模型結構圖
02
模型分析
① 主線程
main thread
執行阻塞Accept,每次用戶端Connect連結過來,
main thread
中accept響應并建立連接配接
② 建立連結成功,得到
Connfd1
套接字後, 依然在
main thread
串行處理套接字讀寫,并處理業務。
③ 在②處理業務中,如果有新用戶端
Connect
過來,
Server
無響應,直到目前套接字全部業務處理完畢。
④ 目前用戶端處理完後,完畢連結,處理下一個用戶端請求。
03
優缺點
優點:
- socket程式設計流程清晰且簡單,适合學習使用,了解socket基本程式設計流程。
缺點:
- 該模型并非并發模型,是串行的伺服器,同一時刻,監聽并響應最大的網絡請求量為
。即并發量為1
。1
- 僅适合學習基本socket程式設計,不适合任何伺服器Server建構。
模型二、單線程Accept+多線程讀寫業務(無I/O複用)
01
模型結構圖
02
模型分析
① 主線程
main thread
執行阻塞Accept,每次用戶端Connect連結過來,
main thread
中accept響應并建立連接配接
② 建立連結成功,得到
Connfd1
套接字後,建立一個新線程
thread1
用來處理用戶端的讀寫業務。
main thead
依然回到
Accept
阻塞等待新用戶端。
③
thread1
通過套接字
Connfd1
與用戶端進行通信讀寫。
④ server在②處理業務中,如果有新用戶端
Connect
過來,
main thread
中
Accept
依然響應并建立連接配接,重複②過程。
03
優缺點
優點:
- 基于
支援了并發的特性。模型一:單線程Accept(無IO複用)
- 使用靈活,一個用戶端對應一個線程單獨處理,
處理業務内聚程度高,用戶端無論如何寫,服務端均會有一個線程做資源響應。server
缺點:
- 随着用戶端的數量增多,需要開辟的線程也增加,用戶端與server線程數量
正比關系,一次對于高并發場景,線程數量受到硬體上限瓶頸。1:1
- 對于長連結,用戶端一旦無業務讀寫,隻要不關閉,server的對應線程依然需要保持連接配接(心跳、健康監測等機制),占用連接配接資源和線程開銷資源浪費。
- 僅适合用戶端數量不大,并且數量可控的場景使用。
僅适合學習基本socket程式設計,不适合任何伺服器Server建構。
模型三、單線程多路I/O複用
01
模型結構圖
02
模型分析
① 主線程
main thread
建立
listenFd
之後,采用多路I/O複用機制(如:select、epoll)進行IO狀态阻塞監控。有
Client1
用戶端
Connect
請求,I/O複用機制檢測到
ListenFd
觸發讀事件,則進行
Accept
建立連接配接,并将新生成的
connFd1
加入到
監聽I/O集合
中。
②
Client1
再次進行正常讀寫業務請求,
main thread
的
多路I/O複用機制
阻塞傳回,會觸該套接字的讀/寫事件等。
③ 對于
Client1
的讀寫業務,Server依然在
main thread
執行流程繼續執行,此時如果有新的用戶端
Connect
連結請求過來,Server将沒有即時響應。
④ 等到Server處理完一個連接配接的
Read+Write
操作,繼續回到
多路I/O複用機制
阻塞,其他連結過來重複 ②、③流程。
03
優缺點
優點:
- 單流程解決了可以同時監聽多個用戶端讀寫狀态的模型,不需要
與用戶端的線程數量關系。1:1
- 多路I/O複用阻塞,非忙輪詢狀态,不浪費CPU資源, CPU使用率較高。
缺點:
- 雖然可以監聽多個用戶端的讀寫狀态,但是同一時間内,隻能處理一個用戶端的讀寫操作,實際上讀寫的業務并發為1。
- 多用戶端通路Server,業務為串行執行,大量請求會有排隊延遲現象,如圖中⑤所示,當
占據Client3
流程時,main thread
流程卡在Client1,Client2
等待下次監聽觸發事件。IO複用
模型四、單線程多路I/O複用+多線程讀寫業務
(業務工作池)
01
模型結構圖
02
模型分析
① 主線程
main thread
建立
listenFd
之後,采用多路I/O複用機制(如:select、epoll)進行IO狀态阻塞監控。有
Client1
用戶端
Connect
請求,I/O複用機制檢測到
ListenFd
觸發讀事件,則進行
Accept
建立連接配接,并将新生成的
connFd1
加入到
監聽I/O集合
中。
② 當
connFd1
有可讀消息,觸發讀事件,并且進行讀寫消息
③
main thread
按照固定的協定讀取消息,并且交給
worker pool
工作線程池, 工作線程池在server啟動之前就已經開啟固定數量的
thread
,裡面的線程隻處理消息業務,不進行套接字讀寫操作。
④ 工作池處理完業務,觸發
connFd1
寫事件,将回執用戶端的消息通過
main thead
寫給對方。
03
優缺點
優點:
- 對于
, 将業務處理部分,通過工作池分離出來,減少多用戶端通路Server,業務為串行執行,大量請求會有排隊延遲時間。模型三
- 實際上讀寫的業務并發為1,但是業務流程并發為worker pool線程數量,加快了業務處理并行效率。
缺點:
- 讀寫依然為
單獨處理,最高讀寫并行通道依然為1.main thread
- 雖然多個worker線程處理業務,但是最後傳回給用戶端,依舊需要排隊,因為出口還是
的main thread
Read + Write
模型五、單線程I/O複用+多線程I/O複用
(線程池)
01
模型結構圖
02
模型分析
① Server在啟動監聽之前,開辟固定數量(N)的線程,用
Thead Pool
線程池管理
② 主線程
main thread
建立
listenFd
之後,采用多路I/O複用機制(如:select、epoll)進行IO狀态阻塞監控。有
Client1
用戶端
Connect
請求,I/O複用機制檢測到
ListenFd
觸發讀事件,則進行
Accept
建立連接配接,并将新生成的
connFd1
分發給
Thread Pool
中的某個線程進行監聽。
③
Thread Pool
中的每個
thread
都啟動
多路I/O複用機制(select、epoll)
,用來監聽
main thread
建立成功并且分發下來的socket套接字。
④ 如圖,
thread
監聽
ConnFd1、ConnFd2
,
thread2
監聽
ConnFd3
,
thread3
監聽
ConnFd4
. 當對應的
ConnFd
有讀寫事件,對應的線程處理該套接字的讀寫及業務。
03
優缺點
優點:
- 将
的單流程讀寫,分散到多線程完成,這樣增加了同一時刻的讀寫并行通道,并行通道數量main thread
,N
為線程池N
數量。Thread
- server同時監聽的
數量幾乎成倍增大,之前的全部監控數量取決于ConnFd套接字
的main thread
的最大限制(select 預設為1024, epoll預設與記憶體大小相關,約3~6w不等),是以理論單點Server最高響應并發數量為多路I/O複用機制
(N*(3~6W)
為線程池N
數量,建議與CPU核心成比例1:1)。Thread
- 如果良好的線程池數量和CPU核心數适配,那麼可以嘗試CPU核心與Thread進行綁定,進而降低CPU的切換頻率,提升每個
處理合理業務的效率,降低CPU切換成本開銷。Thread
缺點:
- 雖然監聽的并發數量提升,但是最高讀寫并行通道依然為
,而且多個身處同一個Thread的用戶端,會出現讀寫延遲現象,實際上每個N
的模型特征與Thread
一緻。模型三:單線程多路IO複用
模型五(程序版)、單程序多路I/O複用+多程序多路I/O複用
(程序池)
01
模型結構圖
02
模型分析
與
五、單線程IO複用+多線程IO複用(連結線程池)
無大差異。
不同處
- 程序和線程的記憶體布局不同導緻,
(主程序)不再進行main process
操作,而是将Accept
過程分散到各個Accept
中.子程序(process)
- 程序的特性,資源獨立,是以
如果Accept成功的fd,其他程序無法共享資源,是以需要各子程序自行Accept建立連結main process
-
隻是監聽main process
狀态,一旦觸發讀事件(有新連接配接請求). 通過一些IPC(程序間通信:如信号、共享記憶體、管道)等, 讓各自子程序ListenFd
競争Process
完成連結建立,并各自監聽。Accept
03
優缺點
與
五、單線程IO複用+多線程IO複用(連結線程池)
無大差異。
不同處:
多程序記憶體資源空間占用稍微大一些
多程序模型安全穩定性較強,這也是因為各自程序互不幹擾的特點導緻。
模型六、單線程多路I/O複用+多線程多路I/O複用+多線程
01
模型結構圖
02
模型分析
① Server在啟動監聽之前,開辟固定數量(N)的線程,用
Thead Pool
線程池管理
② 主線程
main thread
建立
listenFd
之後,采用多路I/O複用機制(如:select、epoll)進行IO狀态阻塞監控。有
Client1
用戶端
Connect
請求,I/O複用機制檢測到
ListenFd
觸發讀事件,則進行
Accept
建立連接配接,并将新生成的
connFd1
分發給
Thread Pool
中的某個線程進行監聽。
③
Thread Pool
中的每個
thread
都啟動
多路I/O複用機制(select、epoll)
,用來監聽
main thread
建立成功并且分發下來的socket套接字。一旦其中某個被監聽的用戶端套接字觸發
I/O讀寫事件
,那麼,會立刻開辟一個新線程來處理
I/O讀寫
業務。
④ 但某個讀寫線程完成目前讀寫業務,如果目前套接字沒有被關閉,那麼将目前用戶端套接字
如:ConnFd3
重新加回線程池的監控線程中,同時自身線程自我銷毀。
03
優缺點
優點:
- 在
基礎上,除了能夠保證同時響應的模型五、單線程IO複用+多線程IO複用(連結線程池)
,又能解決最高并發數
局限的問題。讀寫并行通道
- 同一時刻的讀寫并行通道,達到
,一個用戶端可以對應一個單獨執行流程處理讀寫業務,讀寫并行通道與用戶端數量最大化極限
關系。1:1
缺點:
- 該模型過于理想化,因為要求CPU核心數量足夠大。
- 如果硬體CPU數量可數(目前的硬體情況),那麼該模型将造成大量的CPU切換成本浪費。因為為了保證讀寫并行通道與用戶端
的關系,那麼Server需要開辟的1:1
數量就與用戶端一緻,那麼線程池中做Thread
的監聽線程池綁定CPU數量将變得毫無意義。多路I/O複用
- 如果每個臨時的讀寫
都能夠綁定一個單獨的CPU,那麼此模型将是最優模型。但是目前CPU的數量無法與用戶端的數量達到一個量級,目前甚至差的不是幾個量級的事。Thread
總結
綜上,我們整理了7中Server的伺服器處理結構模型,每個模型都有各自的特點和優勢,那麼對于多少應付高并發和高CPU使用率的模型,目前多數采用的是模型五(或模型五程序版,如Nginx就是類似模型五程序版的改版)。
至于并發模型并非設計的越複雜越好,也不是線程開辟的越多越好,我們要考慮硬體的利用與和切換成本的開銷。“模型六”設計就極為複雜,線程較多,但以當今的硬體能力無法支撐,反倒導緻該模型性能極差。是以對于不同的業務場景也要選擇适合的模型建構,并不是一定固定就要使用某個來應用。
感謝觀看!