淺入淺出Netty(三) Netty線程模型

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

Reactor模型

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

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

單Reactor單線程模型

image

從圖中可以看出:

它是由一個線程來接收客戶端的連接,並將該請求分發到對應的事件處理 handler 中

這種模型好處是簡單,壞處卻很明顯,當某個Handler阻塞時,會導致其他客戶端的handler和accpetor都得不到執行,無法做到高性能,只適用於業務處理非常快速的場景

單Reactor多線程模型

image

該模型在事件處理器(Handler)部分採用了多線程(線程池)

相對於第一種模型來說,在處理業務邏輯,也就是獲取到IO的讀寫事件之後,交由線程池來處理,handler收到響應後通過send將響應結果返回給客戶端。這樣可以降低Reactor的性能開銷,從而更專注的做事件分發工作了,提升整個應用的吞吐。

存在的問題:
Reactor承擔所有事件的監聽和響應,只在主線程中運行,可能會存在性能問題。例如併發百萬客戶端連接,或者服務端需要對客戶端握手進行安全認證,但是認證本身非常損耗性能。

於是又有了下面的線程模型。

主從Reactor多線程模型

image
比起第二種模型,它是將Reactor分成兩部分:

  1. mainReactor負責監聽server socket,用來處理網絡IO連接建立操作,將建立的socketChannel指定註冊給subReactor。
  2. subReactor主要做和建立起來的socket做數據交互和事件業務處理操作。通常,subReactor個數上可與CPU個數等同。

Netty的線程模型

通過配置boss和worker線程池的線程個數以及是否共享線程池等方式,Netty的線程模型可以在以上三種Reactor模型之間進行切換

/**
 * 單Reactor單線程模型
 */
public class SingleReactorDemo {
    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup(1);
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(group)//單線程處理
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerHandler());
        serverBootstrap.bind(8000);
    }
}
/**
 * 單Reactor多線程模型
 */
public class SingleReactorMoreThreadDemo {
    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup(1);
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(group)//單線程處理
                .channel(NioServerSocketChannel.class)
                .childHandler(new MoreThreadHandler());
        serverBootstrap.bind(8000);
    }
}

/**
 * 多線程Handler
 */
public class MoreThreadHandler extends ChannelInboundHandlerAdapter {

    private static ExecutorService executors = Executors.newScheduledThreadPool(200);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //讀取數據
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        executors.submit(()->{
            //向客戶端寫數據
            String currentTime = new Date(System.currentTimeMillis()).toString();
            ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
            ctx.write(resp);
            ctx.flush();//刷新後纔將數據發出到SocketChannel
        });
    }
}
/**
 * 主從Reactor多線程模型
 */
public class MoreReactorMoreThreadDemo {
    public static void main(String[] args) {
        // bossGroup表示監聽端口,accept 新連接的線程組,workerGroup表示處理每一條連接的數據讀寫的線程組
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new MoreThreadHandler());
        serverBootstrap.bind(8000);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章