Netty系列文章之Netty線程模型

前言

咱們在使用Netty進行服務端開發的時候,通常來講會定義兩個NioEventLoopGroup線程池,一個"bossGroup"線程池去負責處理客戶端鏈接,一個"workGroup"線程池去負責處理讀寫操做。那麼,咱們爲何要這麼作呢?這樣作的好處是什麼呢?能不能只使用一個NioEventLoopGroup呢?這就是咱們今天要討論的主題——Netty的線程模型java

Reactor線程模型

實際上Netty線程模型就是Reactor模式的一個實現,而Reactor模式又是什麼呢?react

Reactor模式是基於事件驅動開發的,核心組成部分包括Reactor和線程池,其中Reactor負責監聽和分配事件,線程池負責處理事件,而根據Reactor的數量和線程池的數量,又將Reactor分爲三種模型:bootstrap

  • 單線程模型 (單Reactor單線程)
  • 多線程模型 (單Reactor多線程)
  • 主從多線程模型 (多Reactor多線程)

單線程模型

Reactor_1

圖片摘自:gee.cs.oswego.edu/dl/cpjslide…segmentfault

  • Reactor內部經過selector 監控鏈接事件,收到事件後經過dispatch進行分發,若是是鏈接創建的事件,則由Acceptor處理,Acceptor經過accept接受鏈接,並建立一個Handler來處理鏈接後續的各類事件,若是是讀寫事件,直接調用鏈接對應的Handler來處理
  • Handler完成read->(decode->compute->encode)->send的業務流程
  • 這種模型好處是簡單,壞處卻很明顯,當某個Handler阻塞時,會致使其餘客戶端的handler和accpetor都得不到執行,沒法作到高性能,只適用於業務處理很是快速的場景

多線程模型

Reactor_2

圖片摘自:gee.cs.oswego.edu/dl/cpjslide…markdown

  • 主線程中,Reactor對象經過selector監控鏈接事件,收到事件後經過dispatch進行分發,若是是鏈接創建事件,則由Acceptor處理,Acceptor經過accept接收鏈接,並建立一個Handler來處理後續事件,而Handler只負責響應事件,不進行業務操做,也就是隻進行read讀取數據和write寫出數據,業務處理交給一個線程池進行處理
  • 線程池分配一個線程完成真正的業務處理,而後將響應結果交給主進程的Handler處理,Handler將結果send給client (下面是核心代碼)

Reactor_Handler_Pool

單Reactor承當全部事件的監聽和響應,而當咱們的服務端遇到大量的客戶端同時進行鏈接,或者在請求鏈接時執行一些耗時操做,好比身份認證,權限檢查等,這種瞬時的高併發就容易成爲性能瓶頸多線程

主從多線程模型 (最流行)

Reactor_3

圖片摘自:gee.cs.oswego.edu/dl/cpjslide…併發

  • 存在多個Reactor,每一個Reactor都有本身的selector選擇器,線程和dispatch
  • 主線程中的mainReactor經過本身的selector監控鏈接創建事件,收到事件後經過Accpetor接收,將新的鏈接分配給某個子線程
  • 子線程中的subReactor將mainReactor分配的鏈接加入鏈接隊列中經過本身的selector進行監聽,並建立一個Handler用於處理後續事件
  • Handler完成read->業務處理->send的完整業務流程

Reacotr_Mutiple

Netty中的線程模型與Reactor的聯繫

Netty主要靠NioEventLoopGroup線程池來實現具體的線程模型的ide

單線程模型

單線程模型就是隻指定一個線程執行客戶端鏈接和讀寫操做,也就是在一個Reactor中完成,對應在Netty中的實現就是將NioEventLoopGroup線程數設置爲1,核心代碼是:高併發

NioEventLoopGroup group = new NioEventLoopGroup(1);
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(group)
                .channel(NioServerSocketChannel.class)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childHandler(new ServerHandlerInitializer());
複製代碼

它的工做流程大體以下:oop

Reactor_Netty_1

上述單線程模型就對應了Reactor的單線程模型

多線程模型

多線程模型就是在一個單Reactor中進行客戶端鏈接處理,而後業務處理交給線程池,核心代碼以下:

NioEventLoopGroup eventGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(eventGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new ServerHandlerInitializer());
複製代碼

走進group方法能夠發現咱們平時設置的bossGroup和workerGroup就是使用了同一個group

@Override
public ServerBootstrap group(EventLoopGroup group) {
    return group(group, group);
}
複製代碼

工做流程以下:

Reactor_Netty_2

主從多線程模型 (最常使用)

主從多線程模型是有多個Reactor,也就是存在多個selector,因此咱們定義一個bossGroup和一個workGroup,核心代碼以下:

NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new ServerHandlerInitializer());
複製代碼

工做流程以下:

Netty_reactor_1

注意:其實在Netty中,bossGroup線程池最終仍是隻會隨機選擇一個線程用於處理客戶端鏈接,與此同時,NioServerSocetChannel綁定到bossGroup的線程中,NioSocketChannel綁定到workGroup的線程中

小結

以上總結了Reactor的三種模型以及Netty中的對應實現,在Netty中,咱們使用的最多的仍是主從多線程模型。關於Reactor的學習,最權威的資料應該是Doug Lea大神的Scalable IO in Java,有興趣的同窗能夠看看

參考資料

相關文章
相關標籤/搜索