一、網路編程Socket API的操作含義
| API |
阻塞 |
非阻塞 |
| connect |
tcp三向交握成功後返回。 |
立即返回,需要通過其他方式判斷TCP串連建立成功還是失敗。 |
| send |
阻塞直到將待發送資料從使用者空間全部放入核心發送緩衝區後返回。 |
立即返回,無論待發送資料是否成功放入核心發送緩衝區。 |
| recv |
阻塞直到資料到達核心接收緩衝區,並將資料從核心複製到使用者空間後返回。 |
立即返回,無論是否有資料到達核心接收緩衝區。 |
| close |
略 |
略 |
二、IO模型劃分 1、linux的IO模型劃分 Linux的IO模型可劃分為:“同步 阻塞IO”、“同步 非阻塞IO”、“ IO複用”、“ 訊號驅動IO”和“ 非同步IO”。 POSIX標準可劃分為:“同步IO”和“非同步IO”。
IO操作分為兩個步驟:“發起IO請求”和“實際的IO操作”。
阻塞/非阻塞:IO操作的“發起IO請求”過程是否阻塞請求線程。
同步/非同步:IO操作的“實際的IO讀寫”過程是否阻塞請求線程。 區別解釋: 1)同步/非同步關注的是
訊息通知的機制,而阻塞/非阻塞關注的是程式(線程)
等待訊息通知時的狀態;
2) 同步的情況下,是由處理訊息者自己去等待訊息是否被觸發,而非同步情況下是由觸發機制來通知處理訊息者; 3) 阻塞與非阻塞主要是程式(線程)等待訊息通知時的狀態角度來說的。 阻塞調用是指調用結果返回之前,當前線程會被掛起,一直處於等待訊息通知不能夠執行其他業務, 函數只有在得到結果之後才會返回。 非阻塞 指在不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回 。
| IO模型 |
是否阻塞 |
是否同步 |
| 阻塞IO |
阻塞 |
同步 |
| 同步非阻塞IO |
非阻塞 |
同步 |
| IO多工 |
阻塞 |
同步 |
| AIO |
非阻塞 |
非同步 |
2、同步阻塞IO(blocking IO)
特點:使用者線程在IO操作執行的兩個步驟中都被阻塞。
優點:資料轉送及時,獲得資料無延遲。 缺點:使用者線程阻塞等待,效能低。
3、同步非阻塞IO(non-blocking IO)
特點: 1)使用者線程發起IO操作請求後,若IO條件不滿足則立即收到失敗響應,使用者線程不會被阻塞。 2)使用者線程需不斷的主動發起IO操作請求詢問核心當前IO條件是否滿足。 3)實際的IO操作拷貝資料到使用者空間的過程中,使用者線程仍然被阻塞。 缺點: 1)使用者線程需不斷輪詢IO條件是否滿足,浪費系統資源。 2)IO任務完成的回應時間增大,導致系統整體輸送量降低。
4、IO多工(IO multiplexing)
特點: 1)select/poll/epoll調用後也會阻塞使用者線程,但是使用者線程是被select/poll/epoll函數阻塞,而非被阻塞在真正的IO系統調用之上。 2)單個線程可以同時處理多個網路連接的IO,select/poll/epoll會不斷輪詢所監聽的所有socket,當某個socket有事件就緒就通知使用者線程。 3)當需要同時處理多個用戶端接入請求時,可以使用多線程或IO多工技術進行處理。其中IO多工最大優勢是系統開銷小(系統在單線程情況下可以同時處理多個用戶端請求)。
5、非同步非阻塞IO(asynchronout IO)
特點: 1) 使用者線程在IO操作執行的兩個步驟中都不被阻塞,可以去處理其他商務邏輯。 2)系統核心將資料複製到使用者空間後,主動向使用者線程發送訊號通知/或調用使用者線程回呼函數,告訴使用者線程它的IO操作執行完成了。
6、五種IO模型的比較
non-blocking IO 與 AIO的區別: 1)在non-blocking IO中,仍然要求使用者線程去主動檢查IO條件是否就緒,並且到資料準備完成後也需要使用者線程主動執行系統調用將資料拷貝到使用者空間。 2)AIO則完全不同,它像是使用者線程將整個IO操作交給了系統核心完成,系統核心做完IO操作後主動發送訊號通知告知使用者線程,在此期間使用者線程不需要主動做任何事情。
三、常見的服務端模型 1、BIO + 主線程Acceptor + 多線程Worker
通訊過程: 1) 服務端 由一個獨立的Acceptor線程負責監聽(socket.accept)用戶端的串連。 2) Acceptor監聽到用戶端串連請求後,為每個用戶端socket 串連 建立一個新的Worker線程進行鏈路處理。 3)Worker 線程完成對用戶端socket串連請求的處理後,通過輸出資料流返迴響應給用戶端,然後socket串連斷連,Worker線程銷毀。
模型缺點: 1) BIO的讀和寫操作都是同步阻塞的,阻塞時間取決於對端IO線程的處理速度和網路IO的傳輸速度,可靠性差。
2)服務端線程個數與用戶端並發訪問串連數是1:1的關係。
3)隨著用戶端並發訪問量增大,服務端線程個數線性膨脹,系統效能急劇下降。
最佳化方式:
1)服務端通過線程池來處理多個用戶端的接入請求,通過線程池條件約束服務端線程資源。形成“用戶端並發串連M”與“服務端線程池最大線程數N”的比例關係(治標不治本)。
範例程式碼:
netty-study工程com.zhangyiwen.study.bio.multi_thread_server包。 2、NIO多工 + Reactor模式
Reactor模式思想:分而治之+事件驅動
1)分而治之
一個connection裡完整的網路處理過程一般分為accept、read、decode、process、encode、send這幾步。
Reactor模式將每個步驟映射為一個Task,服務端線程執行的最小邏輯單元不再是一次完整的網路請求,而是Task,且採用非阻塞方式執行。
2)事件驅動
每個Task對應一個特定網路事件。當Task準備就緒時,Reactor收到對應的網路事件通知,並將Task分發給綁定了對應網路事件的Handler執行。
Reactor模型圖解:
1)single reactor thread
2)multi reactor threads
採用多個Reactor,每個Reactor在自己單獨線程中執行,可以並行響應多個用戶端的請求事件;
Netty採用類似這種模式,boss線程池就是多個mainReactor,worker線程池就是多個subReactor
模型特點: 1)NIO中Channel是全雙工系統的,Channel比流可以更好地映射底層作業系統的API(UNIX網路編程模型中,底層作業系統的通道都是全雙工系統的,同時支援讀寫操作);
2)用戶端發起的串連操作是非同步,不需要像之前的用戶端那樣被同步阻塞;
3)JDK的Selector在Linux等主流作業系統上通過epoll實現,它沒有串連控制代碼數的限制,使得一個Reactor線程可以同時處理成千上萬個用戶端串連,而且效能不會隨用戶端串連的增加而線性下降,適合做高效能高負載的網路伺服器方案。
範例程式碼:
netty-study工程com.zhangyiwen.study.nio.reactor_demo包。
四、常見的用戶端模型 1、阻塞的Send/Recv模式 模型優點: 使用Send/Recv編寫簡單,適合於同步介面的封裝。 模型缺點: 不適合做成非同步介面,在Recv的時候該線程不能處理其它業務。 2、多工模式 模型優點: 1)既可封裝成同步介面,也可以封裝成非同步CallBack介面,擴充性更強。 2)非同步CallBack介面 可以進行多個請求發送,有資料可Recv時才處理。 五、用戶端庫的並行調用 1、阻塞Send/Recv模式+線程池
模型優點:
線程池使用比較方便。
模型缺點:
依賴線程的數量,設定太少,並發處理能力太弱,設定太多,線程切換頻繁。
2、NIO多工+串連池 模型優點:
可以避免頻繁的線程切換。
模型缺點:
如果官方的庫沒有提供這種功能,就只能自己去解析協議,沒有線程池使用起來方便快捷。
3、串連複用 模型優點:
一般我們自訂的二進位協議,協議設計時都會帶有一個唯一的序號,Rsp通過這個序號來找到對應哪個Req。
這樣就可以複用一條連結進行多次請求發送和響應接收,無需使用線程池和串連池方式了。