簡述 高性能Linux服務器 模型架構 設計

主要從三個方面進行分析:編程

1.事件處理模式服務器

2.併發模式
多線程

一.事件處理模式併發

1.Reactoor模式異步

定義:socket

  主線程只負責監聽文件描述符上是否有事件發生,有的話當即將該事件通知工做線程,除此以外,主線程不作任何實質性的工做,讀寫數據,接受新的鏈接以及處理客戶請求均在工做線程中完成函數

樣例:性能

  使用同步IO模型epoll_wait爲例實現Reactor模式的工做流程:線程

1).主線程往epoll內核事件表中註冊socket上的讀就緒事件對象

2).主線程調用epoll_wait等待socket上有數據可讀

3).當socket上有數據可讀時,epoll_wait通知主線程,主線程則將socket可讀事件放入請求隊列

4).睡眠在請求隊列上的某個工做線程被喚醒,它從socket讀取數據,並處理客戶請求,而後往epoll內核事件表中註冊該socket上的寫就緒事件

5).主線程調用epoll_wait等待socket可寫

6).當socket可寫時,epoll_wait通知主線程,主線程將socket可寫事件放入請求隊列

7).睡眠在請求隊列上的某個工做線程被喚醒,它往socket上寫入服務器處理客戶請求的結果

 

2.Proactor模式

定義:

  全部的IO操做都交給主線程和內核來處理,工做線程僅僅負責業務邏輯

樣例:

  使用異步IO模型(aio_read和aio_write)爲例實現Proactor模式的工做流程

1).主線程調用aio_read函數向內核註冊socket上的讀完成事件,並告訴內核用戶讀緩衝區的位置,以及讀操做完成時如何通知應用程序(信號)

2).主線程繼續處理其餘邏輯

3).當socket上的數據被讀入用戶緩衝區後,內核將向用戶程序發送一個信號,以通知應用程序數據已經可用

4).應用程序預先定義好的信號處理函數選擇一個工做線程來處理客戶的請求,工做線程處理完客戶的請求以後,調用aio_write函數向內核註冊socket上的寫完成事件,並告訴內核用戶寫緩衝區的位置,以及寫操做完成時如何通知應用程序

5).主線程繼續處理其餘邏輯

6).當用戶緩衝區的數據被寫入socket以後,內核將嚮應用程序發送一個信號,已通知應用程序數據發送完畢

7).應用程序預先定義好的信號處理函數選擇一個工做線程來作善後處理,好比決定是否關閉socket

 

3.使用同步IO的方式模擬Procator模式

定義:

  主線程執行數據讀寫操做,讀寫完成後,主線程向工做線程通知這一完成事件,那麼從工做線程的角度來看,他們就直接得到了數據的讀寫結果,接下來只須要對讀寫結果進行邏輯處理

樣例:

  使用同步模型(以epoll_wait爲例)模擬出Proactor模式

1).主線程往內核事件表中註冊socket上的讀就緒事件

2).主線程調用epoll_wait等待socket上有可讀數據

3).當socket上有可讀數據時,epoll通知主線程,主線程從socket循環讀取數據,直到沒有更多的數據可讀,而後將讀取到的數據封裝成一個請求對象並插入到請求隊列

4).睡眠在請求隊列上的某個工做線程被喚醒,它得到請求對象並處理客戶請求,而後往epoll內核事件表中註冊socket上的寫就緒事件

5).主線程調用epoll_wait等待socket可寫

6).當socket可寫時,epoll_wait通知主線程,主線程往socket上寫入服務器處理客戶請求的結果


ps:Reactor模式註冊的是文件描述符的就緒事件,而Procator模式註冊的是完成事件

 

二.併發模式

1.併發編程存在的意義及背景介紹:

  對計算密集型程序來講,併發編程並無任何優點

  對IO密集型程序來講,併發編程具備很是大的優點,可用顯著的提升性能,因爲IO操做的速度遠遠沒有CPU計算速度快,因此讓程序阻塞於IO操做將浪費大量的CPU時間,若是採用多線程,那麼在這個時間段,CPU就可用去作更加有意義的事情,而不是傻傻的等待IO操做的完成


在IO模型和併發模型中,同步和異步具備不一樣的意義

IO模型中:同步和異步區分的是內核嚮應用程序通知的是何種IO事件(就緒事件仍是完成事件),以及由誰來完成IO讀寫(應用程序/內核)

併發模型中:同步和異步指的是程序的執行順序(徹底按照代碼的順序執行/執行順序由系統事件來驅動)

按照同步方式運行的線程叫作同步線程,效率低,實時性差,但邏輯簡單

按照異步方式運行的線程叫作異步線程,效率高,實時性強,但邏輯複雜

對於服務器這種既要求實時性又要求能同時處理多個客戶請求的應用程序,能夠同時採用同步線程和異步線程的方式實現,即半同步半異步模式


2.半同步半異步模式

概念:

  該模式中,同步線程用來處理客戶邏輯,異步線程用於處理IO事件

  異步線程監聽到客戶請求後,就將其封裝成請求對象並插入請求隊列中,請求隊列將通知某個工做在同步模式的工做線程來讀取並處理請求對象


在服務器程序中,若是考慮到事件處理模式和IO模型,那麼半同步半異步模式存在好幾種變種模式:半同步半異步的Reactor模式,半同步半異步的Procator模式


2.1 半同步半異步的Reactor模式(其實就是上面的Reactoor模式的樣例)

概念:

  異步線程只有一個,由主線程來充當

  主線程負責監聽全部socket上的事件

  若是監聽socket上有讀事件發生,即有新的鏈接到來,主線程接受新鏈接的socket,而後往epoll內核事件註冊表中註冊該socket上的讀寫事件

  若是鏈接socket上有讀寫事件發生,即有新的客戶請求到來或者有數據要發送至客戶端,主線程就將該就緒的鏈接socket插入到請求隊列中

  全部工做線程都睡眠在請求隊列上,當有任務來到時,他們將經過競爭得到任務的接管權(空閒的工做線程來處理任務)


2.2 半同步半異步的Procator模式(其實就是上面的Procator模式的樣例)

概念:

  和半同步半異步的Reactor模式的不一樣之處在於半同步半異步的Reactor模式的主線程插入請求隊列的是就緒的鏈接socket,而半同步半異步的Procator模式的主線程則會將應用程序數據,任務類型等信息封裝成一個任務對象而後將其插入任務隊列,兩種模式的根本區別在於插入任務隊列的東西不同,一個是就緒的事件,一個是事件的結果


半同步半異步模式的缺陷:

1).請求隊列須要加鎖進行保護,不管是插入任務仍是拉取任務都須要得到任務隊列的鎖,這將白白耗費CPU時間

2).工做線程數量少則沒法知足需求,工做線程數量多則線程的切換將耗費大量CPU時間


2.3 高效的半同步半異步模式

概念:

  每一個工做線程負責一個鏈接socket上的全部IO操做,這樣每一個工做線程都能同時處理多個客戶鏈接(主線程對監聽socket調用epoll_wait,工做線程對鏈接socket調用epoll_wait),不管是工做線程仍是主線程都維持本身的事件循環,各自獨立的監聽不一樣的事件

不存在請求隊列,主線程直接分發鏈接socket給工做線程

大大的減小了線程切換的次數

這種半同步半異步的模式其事件機制既能夠採用Reactor也能夠採用Proactor,能夠主線程先直接讀完socket上到來的數據,而後封裝轉遞給工做線程,也能夠由工做線程本身去讀,建議採用讀完封裝傳遞的方式,這樣響應更快,省去了線程切換的時間

 

3.領導者/追隨者模式

概念:

  多個工做線程輪流得到事件源集合,輪流監聽,分發,並處理事件的一種模式

  在任意時間點,程序都僅有一個領導者線程,負責監聽全部IO事件,而其餘線程都是追隨者,追隨者休眠在線程池中等待成爲領導者

  當前領導者若是檢測到IO事件,則先從追隨者中選出一位新的領導者,而後檢測到IO事件的當前領導者處理IO事件,此時,新領導者等待新的IO事件,而原來的領導者處理IO事件,兩者實現了併發

  這樣以來,監聽到IO事件的線程能夠直接去處理IO事件,而不須要轉交給其餘線程從而浪費時間,也不須要將就緒IO事件直接放入請求隊列或者讀完封裝放入請求隊列中,再也不須要請求隊列,也不須要給請求隊列加鎖

  可是須要給線程集加鎖,由於推選新的領導者和等待成爲新的領導者都須要操做線程集,必須加鎖避免競態條件

  一樣這種模式也僅僅支持一個事件源集合,沒法像高效半同步半異步模式那樣讓每一個工做線程獨立管理多個客戶鏈接 

  但總的來講仍是很是高效的

 

-----------------------------------------------------------------------------------------------------------------

參考:《Linux高性能服務器編程》遊雙著

相關文章
相關標籤/搜索