1. 程式人生 > 其它 >Netty 執行緒模型

Netty 執行緒模型

Reactor 執行緒模型

由於傳統 的阻塞 IO 對於響應時間不是很好,因此引入了 Reactor 的非同步事件模型來提高響應時間。

主要存在以下三種方式:

  • 單執行緒

    Reactor 內部通過 selector 來監聽連線事件,收到事件之後通過 dispatcher 來進行分發。如果是連線建立的事件,則由 acceptor 進行處理,acceptor 通過接受到連線,建立一個 Handler 來處理後續的事件。

    ​ 該模型的缺點:由於是使用單執行緒的方式來處理每個連線事件,因此無法有效地利用 CPU 提供的資源

  • 多執行緒

    ​ 為了充分利用 CPU 提供的資源,因此使用多執行緒的方式來對工作集合進行處理。

    ​ 在主執行緒中,Reactor 物件通過 selector 來監控連線事件,收到事件之後通過 dispatch 進行分發,如果是連線事件,則由 acceptor 進行處理,acceptor 通過接收到連線,建立一個 Handler 來處理後續的事件。在這個執行緒模型中,該 Handler 只是負責響應事件,不進行任何業務操作,所有的業務操作都放線上程池中進行處理。

    ​ 該模型存在的缺點:在同時接受大量的連線請求時,由於所有的連線請求都是通過單個的 Reactor 物件來進行處理的,因此這是很可能會造成連線超時的情況。

  • 主從多執行緒

​ 由於使用單獨的 Reactor 來處理連線在處理大量連線是可能會導致連線連線超時的情況,因此在這個執行緒模型中採用了 “主—從” Reactor

的模式來解決多執行緒模型中出現的問題。現在在這個模型中,存在一個 “主” Reactor 物件,它負責將接收到的請求分發到某個 “從” Reactor 物件,“從” Reactor 物件將會將得到的請求按照 “多執行緒模型” 的方式對任務進行處理。“從” Reactor 物件的數量由具體的硬體環境來決定。

Netty 執行緒模型

Netty 通過 NioEventLoopGroup 來實現上文提及的幾種 Reactor 模型

  • 單執行緒模型

    單執行緒模型就是隻指定一個執行緒執行客戶端的連線和讀取操作,即將所有的請求和任務的處理都放在一個執行緒中執行,只要將 NioEventLoopGroup

    中的執行緒數設定為 1 即可實現單執行緒模型。

    NioEventLoopGroup group = new NioEventLoopGroup(1); // 設定 NioEventLoopGroup 執行緒數為1,將當前的 Reactor 執行緒模型設定為單執行緒模型
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(group)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true) // 開啟 Nagle 演算法
        .option(ChannelOption.SO_BACKLOG, 1024) // 最大連線等待佇列的長度
        .childHandler(new SimpleChannelInboundHandler<SocketChannel>() {
            @Override
            protected void
                channelRead0(
                ChannelHandlerContext ctx,
                SocketChannel msg
            ) throws Exception {
                /*
                	TODO
                */
            }
        });
    

    大致的工作流程:

  • 多執行緒模型

    多執行緒模型就是在一個 Reactor 物件中對客戶端的連線進行處理,然後將業務交給執行緒池進行處理。程式碼如下所示:

    NioEventLoopGroup group = new NioEventLoopGroup();
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(group)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new SimpleChannelInboundHandler<SocketChannel>() {
            @Override
            protected void channelRead0( ChannelHandlerContext ctx, SocketChannel msg) throws Exception {
                /*
                	TODO
                */
            }
        });
    

    值得注意的是,這種模型的實現原理就是將“主” Reactor 物件和 “從” Reactor 物件設定為同一個 NioEventLoopGroup 物件來實現的

    具體工作流程:

  • 主從多執行緒模型

    只要將 “主” Reactor 物件和 “從” Reactor 物件設定為不同的 NioEventLoopGroup 即可達到對應的效果。

    具體程式碼如下所示:

    NioEventLoopGroup mainGroup = new NioEventLoopGroup();
    NioEventLoopGroup minorGroup = new NioEventLoopGroup();
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(mainGroup, minorGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new SimpleChannelInboundHandler<SocketChannel>() {
            @Override
            protected void
                channelRead0(
                ChannelHandlerContext ctx,
                SocketChannel msg
            ) throws Exception {
                /*
                	TODO
                 */
            }
        });
    

    工作流程如下所示:

    在 Netty 中,“主” Reactor 實際上依舊只是隨機選擇一個執行緒用於處理客戶端的連線。與此同時, NioServerSocketChannel 繫結到 mainGroup,而 NioSockerChannel 繫結到 minorChannel

參考:

[1] https://juejin.cn/post/6844903974298976270

[2] http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf