經常使用高併發網絡線程模型設計及mongodb線程模型優化實踐

前言:
    服務端一般須要支持高併發業務訪問,如何設計優秀的服務端網絡IO工做線程/進程模型對業務的高併發訪問需求起着相當重要的核心做用。
mysql

     本文總結了了不一樣場景下的多種網絡IO線程/進程模型,並給出了各類模型的優缺點及其性能優化方法,很是適合服務端開發、中間件開發、數據庫開發等開發人員借鑑。nginx

經常使用高併發網絡線程模型設計及mongodb線程 模型優化實踐(最全高併發網絡IO線程模型設計)git

1. 線程模型一. 單線程網絡IO複用模型github

1.1 說明:web

   1. 全部網絡IO事件(accept事件、讀事件、寫事件)註冊到epoll事件集redis

   2. 主循環中經過epoll_wait一次性獲取內核態收集到的epoll事件信息,而後輪詢執行各個事件對應的回調。sql

   3.  事件註冊、epoll_wait事件獲取、事件回調執行所有由一個線程執行mongodb

1.2 該網絡線程模型缺陷數據庫

    1.  全部工做都由一個線程執行,只要任一一個請求的事件回調處理阻塞,其餘請求都會阻塞。例如redis的hash結構,若是filed過多,例如一個hash key包含數百萬filed,則該Hash key過時的時候,整個redis阻塞。apache

   2. 單線程工做模型,CPU會成爲瓶頸,若是QPS超過10萬,整個CPU負載會達到100%。

1.3 典型案例

    1. redis緩存

1.4 主循環工做流程:

while (1) {
	//epoll_wait等待網絡事件,若是有網絡事件則返回,或者超時範圍
	size_t numevents=  epoll_wait();

	//遍歷前面epoll獲取到的網絡事件,執行對應事件回調
	for (j = 0; j < numevents; j++) {
         if(讀事件) {
            //讀數據
	        readData()
            //解析
            parseData()
	        //讀事件處理、讀到數據後的業務邏輯處理
	        requestDeal()
         } else if(寫事件) {
	        //寫事件處理,寫數據邏輯處理
	        writeEentDeal()
         } else {
             //異常事件處理
             errorDeal()
         }
	 }
}

說明:後續多線程/進程模型中,每一個線程/進程的主流程和該while()流程一致。

2. 線程模型二. 單listener+固定worker線程

  1. listener線程負責接受全部的客戶端連接

  2. listener線程每接收到一個新的客戶端連接產生一個新的fd,而後經過分發器發送給對應的工做線程(hash方式)

  3. 工做線程獲取到對應的新連接fd後,後續該連接上的全部網絡IO讀寫都由該線程處理

  4. 假設有32個連接,則32個連接創建成功後,每一個線程平均處理4個連接上的讀寫、報文處理、業務邏輯處理

1.5 redis源碼分析及異步網絡IO複用精簡版demo

因爲以前工做須要,須要對redis內核作二次優化開發,所以對整個redis代碼作了部分代碼註釋,同時把redis的網絡模塊獨立出來作成了簡單demo,該demo對理解epoll網絡事件處理及Io複用實現會有幫助,代碼比較簡短,能夠參考以下地址:

redis源碼詳細註釋分析

redis網絡模塊精簡版demo

推特緩存中間件twemproxy源碼分析實現

2.1 該網絡線程模型缺陷

   1.  進行accept處理的listener線程只有一個,在瞬間高併發場景容易成爲瓶頸
   2. 一個線程經過IO複用方式處理多個連接fd的數據讀寫、報文解析及後續業務邏輯處理,這個過程會有嚴重的排隊現象,例如某個連接的報文接收解析完畢後的內部處理時間過長,則其餘連接的請求就會阻塞排隊

2.2 典型案例

   memcache緩存,適用於內部處理比較快的緩存場景、代理中間場景。memcache源碼實現中文分析能夠詳見: memcache源碼實現分析

 

3. 線程模型三. 固定worker線程模型

   該模型原型圖以下:

說明:

   1. Linux kernel 3.9開始支持reuseport功能,內核協議棧每獲取到一個新連接自動均衡分發給用戶態worker線程。

   2. 該模型解決了模型一的listener單點瓶頸問題

3.1 該網絡線程模型缺陷

      reuseport支持後,內核經過負載均衡的方式分發不一樣新連接到多個用戶態worker進程/線程每一個進程/線程經過IO複用方式處理多個客戶端新連接fd的數據讀寫、報文解析、解析後的業務邏輯處理每一個工做進程/線程同時處理多個連接的請求若是某個連接的報文接收解析完畢後的內部處理時間過長,則其餘連接的請求就會阻塞排隊

     該模型雖然解決了listener單點瓶頸問題可是工做線程內部的排隊問題沒有解決。

     不過,Nginx做爲七層轉發代理,因爲都是內存處理,因此內部處理時間比較短,因此適用於該模型。

3.2 典型案例

    1. nginx(nginx用的是進程,模型原理同樣),該模型適用於內部業務邏輯簡單的場景,如nginx代理等

    2. reuseport支持性能提高過程能夠參考我另外一篇分享:     http://www.javashuo.com/article/p-ofgalmlt-eh.html

    Nginx多進程高併發、低時延、高可靠機制在緩存(redis、memcache)twemproxy代理中的應用

    nginx源碼中文註釋分析

4. 線程模型四. 一個連接一個線程模型

    該線程模型圖以下圖:

說明:

   1. listener線程負責接受全部的客戶端連接

   2. listener線程每接收到一個新的客戶端連接就建立一個線程,該線程只負責處理該連接上的數據讀寫、報文解析、業務邏輯處理。

4.1 該網絡線程模型缺陷:

   1. 一個連接建立一個線程,若是10萬個連接,那麼就須要10萬個線程,線程數太多,系統負責、內存消耗也會不少

   2. 當連接關閉的時候,線程也須要銷燬,頻繁的線程建立和消耗進一步增長系統負載

4.2 典型案例:

   1. mysql默認方式、mongodb同步線程模型配置,適用於請求處理比較耗時的場景,如數據庫服務

   2. Apache web服務器,該模型限制了apache性能,nginx優點會更加明顯

5. 線程模型五. 單listener+動態worker線程(單隊列)

    該線程模型圖以下圖所示:

說明:

  1. listener線程接收到一個新連接fd後,把該fd交由線程池處理,後續該連接的全部讀寫、報文解析、業務處理都由線程池中多個線程處理。

  2. 該模型把一次請求轉換爲多個任務(網絡數據讀寫、報文解析、報文解析後的業務邏輯處理)入隊到全局隊列,線程池中的線程從隊列中獲取任務執行。

  3. 同一個請求訪問被拆分爲多個任務,一次請求可能由多個線程處理。

 4. 當任務太多,系統壓力大的時候,線程池中線程數動態增長

 5. 當任務減小,系統壓力減小的時候,線程池中線程數動態減小

5.1 工做線程運行時間相關的幾個統計:

     T1:調用底層asio庫接收一個完整mongodb報文的時間

     T2:接收到報文後的後續全部處理(含報文解析、認證、引擎層處理、發送數據給客戶端等)

     T3: 線程等待數據的時間(例如:長時間沒有流量,則如今等待讀取數據)

5.2單個工做線程如何判斷本身處於」空閒」狀態:

       線程運行總時間=T1 + T2 +T3,其中T3是無用等待時間。若是T3的無用等待時間佔比很大,則說明線程比較空閒。工做線程每一次循環處理後判斷有效時間佔比,若是小於指定閥值,則本身直接exit退出銷燬

5.3 如何判斷線程池中工做線程「太忙」:

      控制線程專門用於判斷線程池中工做線程的壓力狀況,以此來決定是否在線程池中建立新的工做線程來提高性能。

     控制線程每過必定時間循環檢查線程池中的線程壓力狀態,實現原理就是簡單的實時記錄線程池中的線程當前運行狀況,爲如下兩類計數:總線程數_threadsRunning、當前正在運行task任務的線程數_threadsInUse。若是_threadsRunning=_threadsRunning,說明全部工做線程當前都在處理task任務,線程池中線程壓力大,這時候控制線程就開始增長線程池中線程數。該模型詳細源碼實現過程更多細節詳見:https://my.oschina.net/u/4087916/blog/4295038

5.4 該網絡線程模型缺陷:

     1.  線程池獲取任務執行,有鎖競爭,這裏就會成爲系統瓶頸

5.5 典型案例:

5.5 典型案例:

    mongodb動態adaptive線程模型,適用於請求處理比較耗時的場景,如數據庫服務

    該模型詳細源碼優化分析實現過程參考:

     http://www.javashuo.com/article/p-obucbjcr-nu.html

     Mongodb網絡傳輸處理源碼實現及性能調優-體驗內核性能極致設計

 

6. 線程模型六. 單listener+動態worker線程(多隊列)

     該線程模型圖以下:

說明:

        把一個全局隊列拆分爲多個隊列,任務入隊的時候按照hash散列到各自的隊列,工做線程獲取獲取任務的時候,同理經過hash的方式去對應的隊列獲取任務,經過這種方式減小鎖競爭,同時提高總體性能。

6.1 典型案例:

        OPPO自研mongodb內核多隊列adaptive線程模型優化,性能有很好的提高,適用於請求處理比較耗時的場景,如數據庫服務。該模型詳細源碼優化分析實現過程參考:https://my.oschina.net/u/4087916/blog/4295038

相關文章
相關標籤/搜索