第八章 高性能服務器編程框架數據庫
這一章主要介紹服務器的三個主要模塊: I/O處理單元、邏輯單元、存儲單元。另外服務器的模型有:C/S模型和P2P模型。雖然服務器模型比較多,可是其核心框架都同樣,只是在於邏輯處理方面。以下圖簡單的介紹一臺服務器或服務器機羣模型的基本框架:編程
一、I/O模型緩存
I/O處理單元:I/O處理單元是服務器管理客戶鏈接的模塊。主要是等待並受理新的客戶鏈接接收客戶數據,將服務器響應數據返回給客戶端。服務器
邏輯單元:就是一個個進程或者線程。用於處理客戶數據,將結果傳遞給I/O處理單元或者直接發送給客戶端。服務器中一般由多個邏輯單元,以實現多個客戶端任務的並行處理。網絡
網絡存儲單元:網絡存儲單元用於存儲數據庫、緩存以及文件。框架
請求隊列:請求隊列是各個單元之間的通訊方式的抽象。請求隊列一般被實現爲池的一部分。請求隊列是各臺服務器之間預先創建的靜態的永久的TCP鏈接。異步
I/O模型 有阻塞I/O模型和 非阻塞I/O模型。由於socket在建立的時候默認是阻塞的,在建立socket的時候講第二個參數設置爲SOCK_NONBLOCK標誌,或經過fcntl系統調用 F_SETFL 命令,將其設置爲非阻塞的。對於阻塞I/O執行的系統調用會由於沒法當即完成而被操做系統掛起,直到等待事情發生爲止。好比:1)客戶端經過connect向服務器端發起鏈接,connect將發送同步報文段給服務器而後等待服務器返回確認報文段。2)若是服務器確認報文段沒有當即到達客戶端,則connect調用將被掛起,直到客戶端收到確認報文段喚醒connect調用。在socket的基礎API中,可能被阻塞的系統調用包括 accept send recv 和 connect.socket
對於非阻塞I/O老是須要和其餘I/O通知機制一塊兒使用,若是不和其餘通知機制一塊兒使用仍是阻塞的。好比:I/O複用 和 SIGIO 信號等 另行去處理I/O,處理是異步的。 在非阻塞I/O執行系統調用老是當即返回一個(通知事件結果)。無論事件是否已經發生都會返回。因此對於非阻塞I/O就須要根據errno來區分紅功仍是失敗的狀況。事件返回的結果類型有(再來一次 EAGAIN) (指望阻塞 EWOULDBLOCK) (在處理中 EINPROGRESS) 。函數
I/O複用是最經常使用的 I/O通知機制。例如:應用程序經過I/O複用函數向內核註冊一組事件,內核經過I/O複用函數把其中就緒的事件通知給應用程序。Linux上經常使用的I/O複用函數有 select pull epoll_wait. 它們能提升程序效率的緣由在於它們具備同時監聽多個I/O事件能力。性能
理論上來講,阻塞I/O和 I/O複用 以及信號驅動I/O都是屬於同步I/O模型。由於I/O的讀寫操做都是在I/O事件發生以後由應用程序完成的。而異步I/O的讀寫都是當即返回的,不管是否阻塞,由於真正的讀寫操做已經由內核完成了。也就是說:同步I/O模型要求用戶代碼自行執行I/O操做,將數據從內核緩存區讀入用戶緩衝區,或將數據從用戶緩衝區寫入內核緩衝區。而異步I/O機制則有內核來執行I/O操做,數據在內核緩衝區和用戶緩衝區之間的移動是由內核在「後臺」完成的。
能夠總結 爲同步I/O像應用程序通知的是I/O就緒事件、而異步I/O嚮應用程序通知的是I/O完成事件。
I/O模型對比
1)阻塞I/O : 程序阻塞於讀寫函數;
2)I/O複用: 程序阻塞於I/O複用系統調用,但可同時監聽多個I/O事件。對I/O自己的讀寫操做是非阻塞的。
3)SIGIO信號: 信號觸發讀寫就緒事件,用戶陳谷執行讀寫操做。程序沒有阻塞階段。
4)異步I/O: 內核執行讀寫操做並觸發讀寫完成事件。程序沒有阻塞階段。
二、Reactor 和 Proactor 事件處理模式
事件處理的兩種模式:Reactor 和 Proactor 一般服務器程序須要處理三類事件:I/O事件、信號、定時事件。同步I/O模型一般使用 Reactor模式,異步I/O模式用Proactor處理。也能夠經過同步I/O模擬出 Proactor模式;
Reactor模式:她只要求主線程(i/o處理單元)監聽文件描述上是否有事件發生,有就當即通知通知工做線程(邏輯單元)處理任務。全部的讀寫處理數據都在線程上執行。使用同步I/O模型(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上寫入服務器處理客戶請求的結果。這是一個環形的操做,以下圖:
Proactor模式:她是將I/O操做都交給主線程和內核處理。工做線程僅負責業務邏輯。使用異步I/O(aio_read和 aio_write爲例) 實現Proactor模式。1)主線程調用aio_read函數向內核註冊socket上的讀完成事件,並告訴內核用戶讀緩衝區的位置,以及讀操做完成時如何通知應用程序。2)主線程繼續處理其餘邏輯。3)當socket上的數據被讀入用戶緩衝區後,內核將嚮應用程序發送一個信號,已通知應用程序數據已可用。4)應用程序預先定義好信號處理函數選擇一個工做線程來處理客戶請求。工做線程處理完客戶請求以後,調用aio_write函數向內核註冊socket上的寫完成事件,並告訴內核用戶寫緩衝區的位置,以及寫操做完成時如何通知應用程序。5)主線程繼續處理其餘邏輯。6)當用戶緩衝區的數據被寫入socket以後,內核將嚮應用程序發送一個信號,以通知應用程序數據已經發送完畢。7)應用程序預先定義好的信號處理函數選擇一個工做線程來作善後處理,決定是否關閉socket.以下圖:
三、池
池有不少種,常見的有 內存池、進程池、線程池、鏈接池。池是在服務器啓動時預先初始化建立好的長鏈接。已達到空間換時間的概念提升效率。固然逾期初始化好的數據對它的大小就難以把控了,固然也能夠動態擴容。