很久沒有寫博客了,近期準備把Netty
源碼啃一遍。在這以前本想直接看源碼,可是看到後面發現其實效率不高,
有些概念仍是有必要回頭再細啃的,特別是其線程模型以及EventLoop
的概念。react
固然在開始以前仍是有務必要對IO模型
要有清晰準確的認識。 傳送門 redis
Netty線程模型中一個很是重要的概念: 事件循環機制(EventLoop)
這個概念在JS
上體現的也很是淋漓盡致,下面在開始介紹netty的線程模型以前,容許我簡單的介紹下事件循環機
制在JS
中的體現瀏覽器
JS
的語言性質: 單線程非阻塞,單線程意味着,js代碼在執行的任什麼時候候,都只有一個主線程來處理全部的任務>。非阻塞則意味着,在進行異步IO任務時不會阻塞主線程,主線程會掛起這個任務,等待異步任務完成再執行對應>的回調。多線程
那麼JS是如何實現單線程非阻塞的呢?JS
引擎遇到一個異步事件後並不會一直等待其返回結果,而是會將此事件>掛起(例如交給瀏覽器去執行請求),主線程會繼續執行方法棧中的其餘任務。以後當異步任務返回結果後,(可>能是瀏覽器?)會將回調函數加入到事件隊列(Task Queue
)中,那麼何時會從事件隊列中取出回調函數執行>呢?當前執行棧中的全部任務都執行完畢,主線程處於閒置狀態時會去查找事件隊列是否有任務待執行,若是有則>將回調函數加入到主線程的方法執行棧中執行,如此反覆,咱們就把這個循環過程稱爲事件循環機制(EventLoop
)。併發
不知道介紹了JS的件循環機制,你們有沒有對Event Loop
有了一個初步的認識,下面我將會着重介紹咱們主角Netty的線程模型及其與Event Loop
的聯繫。異步
Netty的線程模型基於Reactor
,Reactor
的核心在於事件分發,它有三種經典的線程模型(單線程模型,多線程>模型,主從多線程模型),下面咱們會結合Netty
的EventLoop
機制一一介紹函數
單線程模型全局只有一個線程在工做,也就意味着請求的接收,分發,IO讀取寫入等操做都在一個線程中完成,該>模型算得上是最經典的線程模型了,例如redis也是採用的此種單線程模型了。oop
能夠看到上圖中,咱們把一個Reactor線程
能夠認爲是一個EventLoop IO
線程,一個事件循環機制。性能
因爲其線程中的IO讀寫都是基於NIO,理論上全部的IO讀寫操做都不會阻塞EventLoop
線程。因此即便是該單線程>模型,也是足以應付絕大多數的場景。編碼
那麼爲何又會延伸出Reactor多線程模型
呢?
當應用併發量很是大時,例如一個Reactor NIO 線程
須要同時處理成百上千的鏈接時,雖然IO讀寫是非阻塞的,>可是消息的編碼解碼都是須要同步阻塞的,這就致使NIO線程處理速度變慢,最終致使消息積壓,出現性能瓶頸。
基於以上緣由也就演進出了第二種模型Reactor多線程模型
Reactor多線程模型
與Reactor單線程模型
最大的區別就是,有一組Reactor NIO線程
(也就是一組 EventLoop
)來處理IO操做
經過上圖,能夠比較清晰的看到,IO的讀寫操做都由一個Reactor NIO線程池
(對應到EventLoop
也就是EventLoopGroup
)來完成的,而請求的監聽和Accept
則是由另外一個單獨的Reactor線程
來完成。
注意Reactor NIO
線程池中的每個線程都是處理N條鏈路,可是一個鏈路只能有一個線程來處理
多線程的Reactor模型
能夠知足絕大部分的應用場景,一般狀況下,咱們使用Netty使用這種線程模型就OK(建立>兩個NioEventLoopGroup,bossGroup大小爲1,workGroup大小爲CPU*2)。可是有可能會存在某些極少數的狀況,一
個Reactor線程
處理請求的Accept
可能會產生性能瓶頸,例如上百萬的併發鏈接請求。這時候咱們可能就須要採
用第三種模型Reactor主從多線程模型
Reactor主從多線程模型
和Reactor多線程模型
的區別在於本來是一個Reactor線程
處理請求的Accept,變成了
一組Reactor線程
。
對於Reactor主從多線程模型
,其實大多數狀況下咱們並不須要。即便咱們給BossGroup指定了多個線程,最終也>只會選擇其中的一個做爲Accepor的NIO線程,除非在服務端綁定了多個端口的狀況下才會啓用BossGroup的多個線
程。
把Netty
的線程模型以及EventLoop
理解清楚,我的以爲最好的方法仍是順着Netty
的源碼一步一步看,看多了 也就理解了這幾種線程模型分別對應了哪幾種狀況,後面的文章我應該會根據源碼來進一步理解netty