Socket Server的N種併發模型彙總

原創聲明
做者: 劉丹冰Aceld,微信公衆號同名

本文主要介紹常見的Server的併發模型,這些模型與編程語言自己無關,有的編程語言可能在語法上直接透明瞭模型本質,因此開發者不必必定要基於模型去編寫,只是須要知道和了解併發模型的構成和特色便可。git

那麼在瞭解併發模型以前,咱們須要兩個必備的前置知識:github

  • socket網絡編程
  • 多路IO複用機制
  • 多線程/多進程等併發編程理論

模型1、單線程Accept(無IO複用)

(1) 模型結構圖

(2) 模型分析

① 主線程main thread執行阻塞Accept,每次客戶端Connect連接過來,main thread中accept響應並創建鏈接golang

② 建立連接成功,獲得Connfd1套接字後, 依然在main thread串行處理套接字讀寫,並處理業務。編程

③ 在②處理業務中,若是有新客戶端Connect過來,Server無響應,直到當前套接字所有業務處理完畢。安全

④ 當前客戶端處理完後,完畢連接,處理下一個客戶端請求。服務器

(3) 優缺點

優勢微信

  • socket編程流程清晰且簡單,適合學習使用,瞭解socket基本編程流程。

缺點網絡

  • 該模型並不是併發模型,是串行的服務器,同一時刻,監聽並響應最大的網絡請求量爲1。 即併發量爲1
  • 僅適合學習基本socket編程,不適合任何服務器Server構建。

模型2、單線程Accept+多線程讀寫業務(無IO複用)

(1) 模型結構圖

(2) 模型分析

① 主線程main thread執行阻塞Accept,每次客戶端Connect連接過來,main thread中accept響應並創建鏈接多線程

② 建立連接成功,獲得Connfd1套接字後,建立一個新線程thread1用來處理客戶端的讀寫業務。main thead依然回到Accept阻塞等待新客戶端。併發

thread1經過套接字Connfd1與客戶端進行通訊讀寫。

④ server在②處理業務中,若是有新客戶端Connect過來,main threadAccept依然響應並創建鏈接,重複②過程。

(3) 優缺點

優勢

  • 基於模型一:單線程Accept(無IO複用) 支持了併發的特性。
  • 使用靈活,一個客戶端對應一個線程單獨處理,server處理業務內聚程度高,客戶端不管如何寫,服務端均會有一個線程作資源響應。

缺點

  • 隨着客戶端的數量增多,須要開闢的線程也增長,客戶端與server線程數量1:1正比關係,一次對於高併發場景,線程數量收到硬件上限瓶頸。
  • 對於長連接,客戶端一旦無業務讀寫,只要不關閉,server的對應線程依然須要保持鏈接(心跳、健康監測等機制),佔用鏈接資源和線程開銷資源浪費。
  • 僅適合客戶端數量不大,而且數量可控的場景使用。

僅適合學習基本socket編程,不適合任何服務器Server構建。


模型3、單線程多路IO複用

(1) 模型結構圖

(2) 模型分析

① 主線程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複用機制阻塞,其餘連接過來重複 ②、③流程。

(3) 優缺點

優勢

  • 單流程解決了能夠同時監聽多個客戶端讀寫狀態的模型,不須要1:1與客戶端的線程數量關係。
  • 多路I/O複用阻塞,非忙詢狀態,不浪費CPU資源, CPU利用率較高。

缺點

  • 雖然能夠監聽多個客戶端的讀寫狀態,可是同一時間內,只能處理一個客戶端的讀寫操做,實際上讀寫的業務併發爲1。
  • 多客戶端訪問Server,業務爲串行執行,大量請求會有排隊延遲現象,如圖中⑤所示,當Client3佔據main thread流程時,Client1,Client2流程卡在IO複用等待下次監聽觸發事件。

模型4、單線程多路IO複用+多線程讀寫業務(業務工做池)

(1) 模型結構圖

(2) 模型分析

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

(3) 優缺點

優勢

  • 對於模型三, 將業務處理部分,經過工做池分離出來,減小多客戶端訪問Server,業務爲串行執行,大量請求會有排隊延遲時間。
  • 實際上讀寫的業務併發爲1,可是業務流程併發爲worker pool線程數量,加快了業務處理並行效率。

缺點

  • 讀寫依然爲main thread單獨處理,最高讀寫並行通道依然爲1.
  • 雖然多個worker線程處理業務,可是最後返回給客戶端,依舊須要排隊,由於出口仍是main threadRead + Write

模型5、單線程IO複用+多線程IO複用(連接線程池)

(1) 模型結構圖

(2) 模型分析

① 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監聽ConnFd一、ConnFd2, thread2監聽ConnFd3,thread3監聽ConnFd4. 當對應的ConnFd有讀寫事件,對應的線程處理該套接字的讀寫及業務。

(3) 優缺點

優勢

  • main thread的單流程讀寫,分散到多線程完成,這樣增長了同一時刻的讀寫並行通道,並行通道數量NN爲線程池Thread數量。
  • server同時監聽的ConnFd套接字數量幾乎成倍增大,以前的所有監控數量取決於main thread多路I/O複用機制的最大限制(select 默認爲1024, epoll默認與內存大小相關,約3~6w不等),因此理論單點Server最高響應併發數量爲N*(3~6W)(N爲線程池Thread數量,建議與CPU核心成比例1:1)。
  • 若是良好的線程池數量和CPU核心數適配,那麼能夠嘗試CPU核心與Thread進行綁定,從而下降CPU的切換頻率,提高每一個Thread處理合理業務的效率,下降CPU切換成本開銷。

缺點

  • 雖然監聽的併發數量提高,可是最高讀寫並行通道依然爲N,並且多個身處同一個Thread的客戶端,會出現讀寫延遲現象,實際上每一個Thread的模型特徵與模型三:單線程多路IO複用一致。

模型五(進程版)、單進程多路I/O複用+多進程多路I/O複用(進程池)

(1) 模型結構圖

(2) 模型分析

5、單線程IO複用+多線程IO複用(連接線程池)無大差別。

不一樣處

  • 進程和線程的內存佈局不一樣致使,main process(主進程)再也不進行Accept操做,而是將Accept過程分散到各個子進程(process)中.
  • 進程的特性,資源獨立,因此main process若是Accept成功的fd,其餘進程沒法共享資源,因此須要各子進程自行Accept建立連接
  • main process只是監聽ListenFd狀態,一旦觸發讀事件(有新鏈接請求). 經過一些IPC(進程間通訊:如信號、共享內存、管道)等, 讓各自子進程Process競爭Accept完成連接創建,並各自監聽。
(3) 優缺點

5、單線程IO複用+多線程IO複用(連接線程池)無大差別。

不一樣處:

多進程內存資源空間佔用稍微大一些

多進程模型安全穩定型較強,這也是由於各自進程互不干擾的特色致使。


模型6、單線程多路I/O複用+多線程多路I/O複用+多線程

(1) 模型結構圖

(2) 模型分析

① 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從新加回線程池的監控線程中,同時自身線程自我銷燬。

(3) 優缺點

優勢

  • 模型5、單線程IO複用+多線程IO複用(連接線程池)基礎上,除了可以保證同時響應的最高併發數,又能解決讀寫並行通道侷限的問題。
  • 同一時刻的讀寫並行通道,達到最大化極限,一個客戶端能夠對應一個單獨執行流程處理讀寫業務,讀寫並行通道與客戶端數量1:1關係。

缺點

  • 該模型過於理想化,由於要求CPU核心數量足夠大。
  • 若是硬件CPU數量可數(目前的硬件狀況),那麼該模型將形成大量的CPU切換成本浪費。由於爲了保證讀寫並行通道與客戶端1:1的關係,那麼Server須要開闢的Thread數量就與客戶端一致,那麼線程池中作多路I/O複用的監聽線程池綁定CPU數量將變得毫無心義。
  • 若是每一個臨時的讀寫Thread都可以綁定一個單獨的CPU,那麼此模型將是最優模型。可是目前CPU的數量沒法與客戶端的數量達到一個量級,目前甚至差的不是幾個量級的事。

總結

綜上,咱們整理了7中Server的服務器處理結構模型,每一個模型都有各自的特色和優點,那麼對於多少應付高併發和高CPU利用率的模型,目前多數採用的是模型五(或模型五進程版,如Nginx就是相似模型五進程版的改版)。

至於併發模型並不是設計的約複雜越好,也不是線程開闢的越多越好,咱們要考慮硬件的利用與和切換成本的開銷。模型六設計就極爲複雜,線程較多,但以當今的硬件能力沒法支撐,反倒致使該模型性能極差。因此對於不一樣的業務場景也要選擇適合的模型構建,並非必定固定就要使用某個來應用。


關於做者:

mail: danbing.at@gmail.com
github: https://github.com/aceld
原創書籍gitbook: http://legacy.gitbook.com/@aceld

創做不易, 共同窗習進步, 歡迎關注做者, 回覆"zinx"有好禮

做者微信公衆號


文章推薦

開源軟件做品

(原創開源)Zinx-基於Golang輕量級服務器併發框架-完整版(附教程視頻)

(原創開源)Lars-基於C++負載均衡遠程調度系統-完整版

精選文章

典藏版-Golang調度器GMP原理與調度全分析

典藏版-Golang三色標記、混合寫屏障GC模式圖文全分析

最經常使用的調試 golang 的 bug 以及性能問題的實踐方法?

Golang中的Defer必掌握的7知識點

Golang中的局部變量「什麼時候棧?什麼時候堆?」

使用Golang的interface接口設計原則

流?I/O操做?阻塞?epoll?

深刻淺出Golang的協程池設計

Go語言構建微服務一站式解決方案

相關文章
相關標籤/搜索