多路復用是什么?怎么理解?
本文主要涉及為
程序中處理網絡IO時的模型,對于系統內核而言網絡IO模型。這里只做普及使用
前置知識,什么是IO?怎么理解IO
IO其實就是In和Out。中文翻譯是輸入和輸出,只要涉及到輸入和輸出的,我們都可以稱之為IO。
例如你在磁盤中讀取文件,讀取文件為In,輸出到其他地方為Out。
例如你Windows系統在網絡通信時,讀取客戶端的輸入數據包,這叫In,系統將數據包輸出給我們編寫的應用程序,這叫Out。
當然,我們如果成自身程序的角度去看網絡通信的話,我們根據系統內核給予的文件描述符去獲取TCP連接數據時,這就叫In,將數據輸出到其他位置,這叫Out。
所以,我們可以總結,IO其實就是輸入輸出,它是對輸入輸出這個動作的統稱,并不僅限于磁盤、網絡等。
當然,IO相關的設備有很多,比如鼠標、觸摸屏、NFC等
對于系統而言,處理IO有什么模型?
這里的系統,指的是Windows內核程序、Linux內核程序。
我們要知道,一個TCP連接到達服務器后,該連接是由系統內核程序進行管控的,它維護這條連接和上層應用與之交互的過程。
回到正題,我們今天說的是系統內核層面的IO模型情況,至于應用程序層的IO模型我會一筆帶過。
同步模型
畫個圖更好理解:
這張圖中,注意我們的業務程序中線程是需要不停的讀取TCP連接通道中的數據,我們可以稱之為為read操作。而本質上業務程序一直不停的主動讀取數據的這種情況,本質上就是一個同步模型,因為始終是應用程序主動來讀取的。
此時如果我們的線程,read數據沒有的話,一直等待讀取到數據的這個操作,對于我們業務程序而言,就是阻塞模型。而業務程序如果讀取read后發現沒有數據,直接跳過,這個就是非阻塞模型。至于業務程序讀取到數據后,自身如何去操作,其實就是業務層面的同步和異步模型。
說到這里,你應該也明白了,系統內核層面的IO模型和我們業務程序層面的IO模型是可以完全不相關的。舉個例子,我們先不談系統內核層面,只說應用程序,應用程序讀取到數據后,讀取數據的線程用來處理業務,那就是同步模型;而應用程序讀取數據的線程不參與業務處理,只處理網絡連接的事情,當其他業務處理線程執行完業務邏輯后再讓處理連接的線程響應數據,這就是異步模型。
那么此時你會有疑問,每次都要應用程序主動去讀取,這太浪費資源了,應用程序能不能等數據來,而不是主動去找內核要,那么就代表著系統內核級別上有沒有異步模型的存在?
實際上是有的,我們馬上來描述。
異步模型
我們可以假想一下它的模型:
其實真實的內核異步網絡IO的比這個圖大很多,這里為了方便理解,做了一些簡化。
從這張圖我們可以知道,內核要實現異步的網絡IO本身是可以實現的,但要注意,其實內核的異步IO應用并不廣泛,原因如下:
1、內核實現難度增大,出問題的風險更大:內核要實現異步IO要比同步復雜得多,涉及到回調函數、事件循環等概念,要知道內核程序對穩定性要求極高,因為它是整個程序運行最基礎的部分,增加復雜度則意味著增加風險。
2、性能考慮:雖然異步IO理論上可以提高性能,但在某些情況下,它可能不會帶來顯著的性能提升,尤其是當系統已經通過其他方式(如多線程或多路復用)進行了優化時。
3、內核實現限制:在Linux系統中,真正的異步IO模型發展面臨一些挑戰,特別是內核實現上的限制和歷史遺留問題。例如,Linux的io_uring雖然是真在往異步IO發展,但在某些領域(如網絡IO)還是基于epoll這種IO多路復用機制更加穩定和成熟。
等等還有很多理由可以說,總體來說,內核的異步IO應用不廣泛主要是在于復雜度與穩定性上,但還有一個重要因素,也就是IO多路復用模型的實現。
IO多路復用模型
前面鋪墊那么多,就是為了引出IO多路復用模型。
我們前面說了異步IO模型的本質是為了提高性能,而在內核層面上為了提高這個性能付出的代價未免太大了一點,那么就需要產生一個折中的辦法。
這個折中的辦法需要滿足以下條件:
? 1、應用程序不能一直去等待讀取數據,節約線程資源。
? 2、內核方面不能用異步操作,保持原來同步讀的模型。
那么**【多路復用】**就能滿足這兩個條件,多路復用模型如圖所示:
內核提供多路復用器,多路復用器中會監聽各個網絡TCP連接的狀態,然后業務程序方面有專門的線程用來不停往多路復用器處查詢連接狀態,當發現有連接的狀態發生變更后,就通知其他對應的線程去讀取數據。
這樣就達到了我們前面說的兩個條件,應用程序無需一直反復嘗試讀取連接中的數據,而內核也無需實現異步模型。
在目前的環境下,絕大多數系統和應用都是使用這類模型來處理網絡請求,例如Windows的IOCP,Linux的epoll,而業務程序方面代表性最強的則是Netty。
當然,多路復用可不是這一點講清楚的,這里讓大家對多路復用的基本原理有個了解,其實在上圖中每一個涉及的點都有不同的實現方式,可以自行網上搜索了解下。
那么最后做個總結:
? 1、業務程序自身處理IO和內核處理IO是無關的,本身是兩個層面。
? 2、內核層面絕大多數都是同步模型,此時加入多路復用后性能已經足夠當下環境的需求,而異步模型由于復雜度和穩定性問題普及并不廣泛。
? 3、多路復用器不僅僅用于IO方面,其他很多場景都可以使用,所以多路復用器我們要理解其模型,而不能限定于IO。
? 4、在Java層面,多路復用器在JVM的NIO包中已經實現好了,我們只管用就行,而當我們使用了Netty這類集成框架后,大多數情況下我們并不需要關系底層的內容