① 請問reactor模式到底是什麼意思
Reactor模式是網路編程中一種典型的IO線程模型,其核心在於利用NIO技術進行IO事件的監聽與分發。簡單來說,Reactor模式通過IO多路復用技術監聽並持續產生IO事件,這一過程彷彿一個反應堆不斷產生能量,因而得名Reactor模型。Reactor模型在用戶空間中對IO線程進行分工,從而提高IO處理效率。下面將詳細介紹Reactor模型的三種類型:
1. 單Reactor單線程模式:作為老闆的角色,需一人身兼數職,包括接受客戶連接、等待並處理客戶請求、進行業務處理和最後送客等。
2. 單Reactor多線程模式:隨著客戶數量增加,任務量超出了單線程處理能力,於是引入多線程協同工作,以提高效率。
3. 主從Reactor多線程模式:此模式下,根據任務優先順序進行分工,主Reactor負責關鍵的客戶連接管理,從Reactor則處理其他IO事件。
在主從Reactor模式中,從Reactor僅注冊read事件,因為read事件由內核觸發,而write事件則由用戶業務線程根據實際需求決定。此外,netty等主流網路框架普遍採用主從Reactor多線程模型。
另一種基於AIO的IO線程模型稱為Proactor模式。該模式通過內核完成IO事件監聽、執行與結果分發,提供全非同步編程體驗,大大減少了阻塞現象。Proactor模式組件及執行過程將在後續內容中詳細介紹。
Reactor與Proactor模式之間的對比,主要體現在對IO事件的處理方式上。Reactor模型中,用戶線程需主動關注IO就緒事件,並自行讀取數據;而Proactor模型則將這一過程交由內核處理,用戶線程只需等待數據准備就緒。
Netty中的IO模型與上述概念密切相關。它支持單Reactor單線程、單Reactor多線程和主從Reactor多線程三種模型。Netty中的主從Reactor多線程模型注重高效與並發控制,每個Socket連接由一個獨立的PipeLine實例管理,確保線程安全與最大吞吐量。
了解了上述內容後,您將對Reactor模式在實際應用中的作用與實現方式有更深刻的理解。如需進一步了解,歡迎關注公眾號:bin的技術小屋。
② 一文徹底理解BIO、NIO、AIO
深入理解BIO、NIO、AIO
同步並阻塞的IO模型(BIO):在伺服器實現中,一個連接對應一個線程。對於每一個客戶端的連接請求,伺服器需要啟動一個線程進行處理。這種方法簡單直接,但資源消耗較大,特別是當連接數增加時,線程開銷會變得明顯。
同步非阻塞的IO模型(NIO):伺服器實現模式轉變為一個線程處理多個請求。客戶端發送的連接請求都會被注冊到多路復用器上,多路復用器會輪詢連接有I/O請求的任務進行處理。這樣可以有效減少線程的開銷,提高伺服器性能。
非同步非阻塞的IO模型(AIO):一個有效請求一個線程。客戶端的I/O請求先由操作系統完成,再通知應用程序啟動線程進行處理。AIO適用於連接數較多且連接時間較長的應用場景,可以有效避免資源耗盡和進程僵死。
深入剖析java BIO:
單客戶端對單服務端的通信,可以發送單條或多條信息。多個客戶端對單服務端時,同樣可以發送多條信息。總結來說,BIO模型在並發訪問增加時,系統服務端將呈現1:1的線程開銷,導致資源耗盡和進程僵死。
採用偽非同步I/O通信框架,通過線程池和任務隊列實現。客戶端接入時,將Socket封裝成Task交給線程池處理。線程池維護消息隊列和N個活躍線程,處理消息隊列中的Socket任務。這種方式可以有效管理多個客戶端並發訪問,避免資源耗盡和宕機。
NIO基本介紹:
NIO有三大核心:Channel(通道)、Buffer(緩沖區)和Selector(選擇器)。Buffer是用於存儲數據的內存容器,提供一組方法方便地訪問內存。Channel類似於流,可以讀寫數據,支持非阻塞讀寫和非同步操作。Selector可以檢測多個通道的狀態,使一個線程管理多個通道。
Channel通道:
通道用於與緩沖區交互,可以讀取或寫入數據。通道可以非阻塞讀寫,並支持緩沖區讀寫。它是一個介面,提供了數據流的雙向操作。
選擇器Selector:
選擇器用於監控多個通道的狀態,使一個線程管理多個通道的IO操作,提高伺服器性能。
NIO核心二:通道
通道介面提供了讀寫緩沖區數據的方法,支持分散讀取和聚集寫入。常用實現類FileChannel用於本地文件操作,提供了讀寫和復制文件的功能。
NIO核心三:選擇器
選擇器用於多路復用IO操作,一個線程可以同時處理多個通道的事件,提高並發能力。
對比BIO、NIO與AIO:
BIO適用於連接數目小且固定的場景,資源消耗大。NIO適用於連接數目多且輕操作的場景,編程復雜但性能高。AIO適用於連接數目多且重操作的場景,利用OS並發操作,編程復雜但性能優異。
總結NIO非阻塞式網路通信:
採用線程池、Buffer、Channel和Selector實現高效並發處理。NIO提高了伺服器性能和彈性伸縮能力。AIO進一步優化了並發處理效率。
目標實現多人群聊系統:
設計服務端和客戶端代碼實現多人群聊功能,利用NIO非阻塞網路編程機制。
BIO、NIO、AIO適用場景分析:
BIO適用於連接數目小且固定的架構,NIO適用於連接數目多且輕操作的場景,AIO適用於連接數目多且重操作的場景。
③ IO模型及select,poll,epoll和kqueue的區別
(一)首先,介紹幾種常見的I/O模型及其區別,如下:
blocking I/O
nonblocking I/O
I/O multiplexing (select and poll)
signal driven I/O (SIGIO)
asynchronous I/O (the POSIX aio_functions)—————非同步IO模型最大的特點是 完成後發回通知。
阻塞與否,取決於實現IO交換的方式。
非同步阻塞是基於select,select函數本身的實現方式是阻塞的,而採用select函數有個好處就是它可以同時監聽多個文件句柄.
非同步非阻塞直接在完成後通知,用戶進程只需要發起一個IO操作然後立即返回,等IO操作真正的完成以後,應用程序會得到IO操作完成的通知,此時用戶進程只需要對數據進行處理就好了,不需要進行實際的IO讀寫操作,因為真正的IO讀取或者寫入操作已經由內核完成了。
1 blocking I/O
這個不用多解釋吧,阻塞套接字。下圖是它調用過程的圖示:
重點解釋下上圖,下面例子都會講到。首先application調用 recvfrom()轉入kernel,注意kernel有2個過程,wait for data和 data from kernel to user。直到最後 complete後,recvfrom()才返回。此過程一直是阻塞的。
2 nonblocking I/O:
與blocking I/O對立的,非阻塞套接字,調用過程圖如下:
可以看見,如果直接操作它,那就是個輪詢。。直到內核緩沖區有數據。
3 I/O multiplexing (select and poll)
最常見的I/O復用模型,select。
select先阻塞,有活動套接字才返回。與blocking I/O相比,select會有兩次系統調用,但是select能處理多個套接字。
4 signal driven I/O (SIGIO)
只有UNIX系統支持,感興趣的課查閱相關資料
與I/O multiplexing (select and poll)相比,它的優勢是,免去了select的阻塞與輪詢,當有活躍套接字時,由注冊的handler處理。
5 asynchronous I/O (the POSIX aio_functions)
很少有*nix系統支持,windows的IOCP則是此模型
完全非同步的I/O復用機制,因為縱觀上面其它四種模型,至少都會在由kernel data to appliction時阻塞。而該模型是當完成後才通知application,可見是純非同步的。好像只有windows的完成埠是這個模型,效率也很出色。
6 下面是以上五種模型的比較
可以看出,越往後,阻塞越少,理論上效率也是最優。
=====================分割線==================================
5種模型的比較比較清晰了,剩下的就是把select,epoll,iocp,kqueue按號入座那就OK了。
select和iocp分別對應第3種與第5種模型,那麼epoll與kqueue呢?其實也於select屬於同一種模型,只是更高級一些,可以看作有了第4種模型的某些特性,如callback機制。
為什麼epoll,kqueue比select高級?
答案是,他們無輪詢。因為他們用callback取代了。想想看,當套接字比較多的時候,每次select()都要通過遍歷FD_SETSIZE個Socket來完成調度,不管哪個Socket是活躍的,都遍歷一遍。這會浪費很多CPU時間。如果能給套接字注冊某個回調函數,當他們活躍時,自動完成相關操作,那就避免了輪詢,這正是epoll與kqueue做的。
windows or *nix (IOCP or kqueue/epoll)?
誠然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系統,但是由於其系統本身的局限性,大型伺服器還是在UNIX下。而且正如上面所述,kqueue/epoll 與 IOCP相比,就是多了一層從內核數據到應用層的阻塞,從而不能算作asynchronous I/O類。但是,這層小小的阻塞無足輕重,kqueue與epoll已經做得很優秀了。
提供一致的介面,IO Design Patterns
實際上,不管是哪種模型,都可以抽象一層出來,提供一致的介面,廣為人知的有ACE,Libevent(基於reactor模式)這些,他們都是跨平台的,而且他們自動選擇最優的I/O復用機制,用戶只需調用介面即可。說到這里又得說說2個設計模式,Reactor and Proactor。見:Reactor模式--VS--Proactor模式。Libevent是Reactor模型,ACE提供Proactor模型。實際都是對各種I/O復用機制的封裝。
Java nio包是什麼I/O機制?
現在可以確定,目前的java本質是select()模型,可以檢查/jre/bin/nio.dll得知。至於java伺服器為什麼效率還不錯。。我也不得而知,可能是設計得比較好吧。。-_-。
=====================分割線==================================
總結一些重點:
只有IOCP是asynchronous I/O,其他機制或多或少都會有一點阻塞。
select低效是因為每次它都需要輪詢。但低效也是相對的,視情況而定,也可通過良好的設計改善
epoll, kqueue、select是Reacor模式,IOCP是Proactor模式。
java nio包是select模型。。
(二)epoll 與select的區別
1. 使用多進程或者多線程,但是這種方法會造成程序的復雜,而且對與進程與線程的創建維護也需要很多的開銷。(Apache伺服器是用的子進程的方式,優點可以隔離用戶) (同步阻塞IO)
2.一種較好的方式為I/O多路轉接(I/O multiplexing)(貌似也翻譯多路復用),先構造一張有關描述符的列表(epoll中為隊列),然後調用一個函數,直到這些描述符中的一個准備好時才返回,返回時告訴進程哪些I/O就緒。select和epoll這兩個機制都是多路I/O機制的解決方案,select為POSIX標准中的,而epoll為linux所特有的。
區別(epoll相對select優點)主要有三:
1.select的句柄數目受限,在linux/posix_types.h頭文件有這樣的聲明:#define __FD_SETSIZE 1024 表示select最多同時監聽1024個fd。而epoll沒有,它的限制是最大的打開文件句柄數目。
2.epoll的最大好處是不會隨著FD的數目增長而降低效率,在selec中採用輪詢處理,其中的數據結構類似一個數組的數據結構,而epoll是維護一個隊列,直接看隊列是不是空就可以了。epoll只會對"活躍"的socket進行操作---這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。那麼,只有"活躍"的socket才會主動的去調用 callback函數(把這個句柄加入隊列),其他idle狀態句柄則不會,在這點上,epoll實現了一個"偽"AIO。但是如果絕大部分的I/O都是「活躍的」,每個I/O埠使用率很高的話,epoll效率不一定比select高(可能是要維護隊列復雜)。
3.使用mmap加速內核與用戶空間的消息傳遞。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核於用戶空間mmap同一塊內存實現的。
關於epoll工作模式ET,LT
epoll有兩種工作方式
ET:Edge Triggered,邊緣觸發。僅當狀態發生變化時才會通知,epoll_wait返回。換句話,就是對於一個事件,只通知一次。且只支持非阻塞的socket。
LT:Level Triggered,電平觸發(默認工作方式)。類似select/poll,只要還有沒有處理的事件就會一直通知,以LT方式調用epoll介面的時候,它就相當於一個速度比較快的poll.支持阻塞和不阻塞的socket。
三 Linux並發網路編程模型
1 Apache 模型,簡稱 PPC ( Process Per Connection ,):為每個連接分配一個進程。主機分配給每個連接的時間和空間上代價較大,並且隨著連接的增多,大量進程間切換開銷也增長了。很難應對大量的客戶並發連接。
2 TPC 模型( Thread Per Connection ):每個連接一個線程。和PCC類似。
3 select 模型:I/O多路復用技術。
.1 每個連接對應一個描述。select模型受限於 FD_SETSIZE即進程最大打開的描述符數linux2.6.35為1024,實際上linux每個進程所能打開描數字的個數僅受限於內存大小,然而在設計select的系統調用時,卻是參考FD_SETSIZE的值。可通過重新編譯內核更改此值,但不能根治此問題,對於百萬級的用戶連接請求 即便增加相應 進程數, 仍顯得杯水車薪呀。
.2select每次都會掃描一個文件描述符的集合,這個集合的大小是作為select第一個參數傳入的值。但是每個進程所能打開文件描述符若是增加了 ,掃描的效率也將減小。
.3內核到用戶空間,採用內存復制傳遞文件描述上發生的信息。
4 poll 模型:I/O多路復用技術。poll模型將不會受限於FD_SETSIZE,因為內核所掃描的文件 描述符集合的大小是由用戶指定的,即poll的第二個參數。但仍有掃描效率和內存拷貝問題。
5 pselect模型:I/O多路復用技術。同select。
6 epoll模型:
.1)無文件描述字大小限制僅與內存大小相關
.2)epoll返回時已經明確的知道哪個socket fd發生了什麼事件,不用像select那樣再一個個比對。
.3)內核到用戶空間採用共享內存方式,傳遞消息。
四 :FAQ
1、單個epoll並不能解決所有問題,特別是你的每個操作都比較費時的時候,因為epoll是串列處理的。 所以你有還是必要建立線程池來發揮更大的效能。
2、如果fd被注冊到兩個epoll中時,如果有時間發生則兩個epoll都會觸發事件。
3、如果注冊到epoll中的fd被關閉,則其會自動被清除出epoll監聽列表。
4、如果多個事件同時觸發epoll,則多個事件會被聯合在一起返回。
5、epoll_wait會一直監聽epollhup事件發生,所以其不需要添加到events中。
6、為了避免大數據量io時,et模式下只處理一個fd,其他fd被餓死的情況發生。linux建議可以在fd聯繫到的結構中增加ready位,然後epoll_wait觸發事件之後僅將其置位為ready模式,然後在下邊輪詢ready fd列表。