Netty服務端和客戶端創建的源碼分析

<h2>服務端</h2>

以下是Netty官方的一個Echo服務示例:

public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(new EchoServerHandler());
                 }
             });
  
            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();
  
            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
        
    }
}

上面代碼創建Server端服務的流程如下:

  1. 創建ServerBootstrap實例
  2. 設置EventLoopGroup
  3. 設置創建的Channel類型
  4. option配置屬性
  5. 設置Handler,處理請求
  6. 設置ChildHandler,處理對應channel的請求
  7. 通過bind創建Chnnel并綁定,啟動服務

服務端創建時序圖

服務端重要組件
ServerBootstrap
ServerBootstrap是Netty服務端的啟動輔助類,提供一系列的方法用于設置服務端的參數和配置,簡化開發。(衍生一點:ServerBootstrap的構造方法是無參的,因為參數太多所以采用了Builder模式)

繼承自AbstractBootstrap,核心屬性有childGroup和childHandler。

  • childGroup:負責調度和執行客戶端的接入、網絡讀寫事件的處理、用戶自定義任務和定時任務的執行
  • childHandler:自定義的業務Handler

AbstractBootstrap核心屬性有group和handler。

  • group:處理客戶端的鏈接請求,并轉交給childGroup(讀取的數據是穿件的NioSocketChannel)

Reactor線程池
Netty的Reactor線程池是EventLoopGroup,實際上是一個EventLoop的數組。

EventLoop的職責是處理所有注冊到本線程多路復用器Selector上的Channel,Selector的輪訓操作有EventLoop線程run方法驅動。

另外用戶自定義的Task和定時任務Task也由統一的EventLoop負責處理。

Channel
作為Nio服務,需要創建ServerSocketChannel,Netty對原生NIO類庫做了封裝,對應實現類為NioServerSocketChannel。

用戶只需要制定Channel的實現類型,內部通過反射機制來創建對應的實例。

因為只在監聽端口時創建,所以反射的性能影響并不大。

ChannelPipeline
ChannelPipeline是網絡事件處理的職責鏈,負責管理和執行ChannelHandler。網絡事件以事件流的形式在ChannelPipeline中流轉。

ChannelHandler
ChannelHandler是提供給用戶定制和擴展的關鍵接口,包括編解碼,業務處理等都是通過ChannelHandler進行的。

Selector
Selector輪訓操作由NioEventLoop調度和執行,選擇準備就緒的Channel集合。

NioServerSocketChannel
綁定Server端地址的Server,讀取客戶端的鏈接請求(只有一個,在bind時創建)。

NioSocketChannel
和客戶端之間的鏈接。

服務端線程模型

  • mainReactor:parentGroup
  • subReactor:childGroup
  • ThreadPool:如果沒指定,使用childGroup執行,如果指定了則是業務線程(執行業務Handler的線程)

Handler模型

Server啟動的關鍵流程

  1. bind操作創建了NioServerSocketChannel并注冊到NioEventLoop中(parent group中會有一個線程執行selector的輪訓操作)

Server端的請求接入流程

  1. NioEventLoop輪訓到到就緒時間后,調用Unsafe.read(NioMessageUnsafe實現)創建NioSocketChannel并傳遞到ServerBootstrap的ServerBootstrapAcceptor的channelRead方法中。
  2. ServerBootstrapAcceptor的channelRead方法將NioSocketChannel注冊到childGroup的Selector上(實現代碼是AbstractNioSocket的doRegister;之后NioSocketChannel的事件就由child group處理,這點和NioServerSocketChannel的注冊、處理是一樣的;和上面的線程模型也是呼應的:兩個reactor)。

<h2>客戶端</h2>

public static void main(String[] args) throws Exception {
    // Configure SSL.git
    final SslContext sslCtx;
    if (SSL) {
        sslCtx = SslContextBuilder.forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
    } else {
        sslCtx = null;
    }

    // Configure the client.
    EventLoopGroup group = new NioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(group)
         .channel(NioSocketChannel.class)
         .option(ChannelOption.TCP_NODELAY, true)
         .handler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                 if (sslCtx != null) {
                     p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                 }
                 //p.addLast(new LoggingHandler(LogLevel.INFO));
                 p.addLast(new EchoClientHandler());
             }
         });

        // Start the client.
        ChannelFuture f = b.connect(HOST, PORT).sync();

        // Wait until the connection is closed.
        f.channel().closeFuture().sync();
    } finally {
        // Shut down the event loop to terminate all threads.
        group.shutdownGracefully();
    }
}

創建客戶端的大致流程:

  1. 創建Bootstrap實例
  2. 設置EventLoop
  3. 指定Channel類型
  4. option配置
  5. 指定Handler
  6. connect

客戶端創建時序圖

客戶端和服務端的模式基本一致,由線程輪訓Selector的事件,由Pipeline進行事件傳遞,EventLoop進行處理。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容