歡迎來到啾啾的博客🐱。
這是一個致力于構建完善的Java程序員知識體系的博客📚,記錄學習的點滴,分享工作的思考、實用的技巧,偶爾也分享一些雜談💬。
歡迎評論交流,感謝您的閱讀😄。
引言
我們非常有必要了解I/O模型,以Java微服務為例,微服務架構中的網關Gateway與共享緩存Redis的核心設計的理解都離不開網絡I/O。
5種I/O模型
當一次網絡請求發生時,將會按順序經歷“等待數據從遠程主機到達緩沖區”和“將數據從緩沖區復制到應用程序網絡地址空間”兩個階段。
根據實現這兩個階段的不同方法,人們把網絡I/O模型總結為兩類、五種模型:兩類是指同步I/O與異步I/O,五種是指在同步I/O中又劃分出阻塞I/O、非阻塞I/O、多路復用I/O、信號驅動I/O四種細分模型以及異步I/O模型。
這里劃重點,兩個階段🌟,后續5種I/O模型差異的理解基于這兩個階段的理解。
1——“等待數據從遠程主機到達緩沖區”,書中這個描述理解容易有問題,如果劃分成兩個階段,這個階段的“到達”應該是“等待數據從遠程主機到達緩沖區后,經由協議處理,用戶進程可以安全讀取的有效數據存在與內核緩沖區”。
到達緩沖區 = 數據到達——> 協議處理 ——> 數據就緒
2——“將數據從緩沖區復制到應用程序網絡地址空間”
根據UNIX網絡編程中的分類,I/O模型分為5種。
其中,同步與異步概念如下:
- 同步
調用端在發出請求之后,得到結果之前必須一直等待。
比如日常代碼按順序執行就是異步,等待前一行代碼執行完成然后繼續下一行代碼。 - 異步
發出調用請求之后將立即返回,不會馬上得到處理結果,結果將通過狀態變化和回調來通知調用者。
比如使用線程處理任務,主線程繼續執行當前任務,這種就是異步。
阻塞和非阻塞是針對請求處理過程而言,指在收到調用請求之后,返回結果之前,當前處理線程是否會被掛起。
以UDP recvfrom(接收數據并獲取發送方地址的核心函數)操作為例,五種I/O模型解釋如下。
阻塞I/O (Blocking I/O)
等待兩個階段操作都完成,即等待數據到緩沖區、等待數據從緩沖區復制到到應用程序網絡地址空間的操作都完成。
阻塞期間,線程會休眠,狀態切換開銷大。
比如Socket編程就是阻塞I/O。
非阻塞I/O(Non-blocking I/O)
線程定期檢查(輪詢)數據是否就緒(數據是否已經從完成主機到達緩沖區),減少一階段無效等待。
非阻塞I/O輪詢檢查本身也會消耗CPU資源,但可以避免線程休眠,適合一些很快就能返回結果的請求。
需要留意的是,二階段復制數據 copy_to_user的內存拷貝操作仍可能引起微秒級阻塞(取決于數據量大小)。
多路復用I/O(I/O Multiplexing)
單線程監控多個文件描述符。即單一線程處理多個請求,當有任意請求完成階段一進入就緒狀態,則為其執行階段二:開始復制數據。
其中監控多個文件描述符細分為select、poll、kqueue等不同實現。
雖然描述是單個線程的多路復用,但是一般并發處理的設計是“線程池+多路復用”。
信號驅動I/O(Signal-driven I/O)
內核通過信號(信號處理函數)通知就緒狀態。
信號驅動I/O和異步I/O的區別在于,信號驅動是通知階段一完成,可以開始階段二,異步則是通知階段二完成。
異步I/O(Asynchronous I/O)
全流程非阻塞,內核完成所有操作后通知。
異步I/O數據到緩沖區后,不需要由調用進程主動進行緩沖區復制數據到應用程序網絡地址空間的操作,而是復制完成后由操作系統向線程發送信號。
高并發選擇
模型 | 阻塞階段 | 用戶參與度 | 典型應用場景 |
---|---|---|---|
阻塞I/O | 全程阻塞 | 被動等待 | 簡單客戶端程序 |
非阻塞I/O | 數據拷貝階段 | 主動輪詢 | 低并發實時系統 |
I/O多路復用 | select阻塞,拷貝阻塞 | 集中管理 | Web服務器(Nginx) |
信號驅動I/O | 僅拷貝階段 | 異步通知 | 特殊設備監控 |
異步I/O | 無阻塞 | 完全托管 | 高性能服務器(Proactor模式) |
在高并發場景下推薦使用I/O多路復用或異步I/O模型。
相較于其他I/O模型,這兩種模型沒有阻塞I/O模型的休眠成本、沒有非阻塞I/O模型的輪詢成本、也沒有信號I/O模型處理上下文切換的成本。
且多路復用I/O意味著在同樣的線程數下,線程池能處理更多的請求。
而異步I/O相較于多路復用I/O則減少了通知處理次數,且多個請求不用等待一個線程延遲更低。
關于高并發場景,細講還有多路復用優化、異步的io_uring高級特性,還有不同并發量級的選擇策略等。
多路復用瓶頸:就緒事件>線程池處理能力任務堆積怎么辦?
異步瓶頸:SQ(提交隊列)環大小限制批量提交規模(Linux io_uring默認4096 entries)
……
等等,I/O還有很多很多可以學習,本篇先到此為止,感謝您的閱讀。
推薦閱讀
【linux內核】五大經典IO模型(原理+動圖+代碼詳解)