自頂向下深入分析Netty(三)--Bootstrap

Netty的一種線程模型

本文開始分析Netty的源碼,由于目標是自頂向下分析,在這一節(jié)將分析Netty是如何構建起如上圖所示的整體框架。首先將使用一個示例展示怎么使用Bootstarp構建服務端應用,然后將深入源碼了解底層機制和原理。


1.使用示例

首先使用Netty構造如圖所示的框架,源碼如下:

    // 指定mainReactor
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    // 指定subReactor
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    // 用戶自定義的ThreadPool
    EventExecutorGroup threadPool = new ThreadPool();
    try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
         .channel(NioServerSocketChannel.class)
         .option(ChannelOption.SO_BACKLOG, 100) // 設置TCP參數(shù)
         .childHandler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                 p.addLast(threadPool,    
                    new DecoderHandler(),   // 解碼處理器
                    new ComputeHandler());  // 計算處理器
                    new EncoderHandler(),   // 編碼處理器
             }
         });

        // 綁定到本地端口等待客戶端連接
        ChannelFuture f = b.bind(PORT).sync();
        
        // 等待接受客戶端連接的Channel被關閉
        f.channel().closeFuture().sync();
    } finally {
        // 關閉兩個線程組
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        threadPool.shutdown();
    }

逐行分析代碼,EventLoopGroup是Netty實現(xiàn)的線程池接口,兩個線程池:bossGroup和workerGroup分別對應mainReactor和subReactor,其中boss專門用于接受客戶端連接,worker也就是常說的IO線程專門用于處理IO事件。IO事件包括兩類,一類如服務端接收到客戶端數(shù)據(jù)的Read事件,另一類如用戶線程主動向客戶端發(fā)送數(shù)據(jù)的Write事件。在4.0版本中,用戶自定義的業(yè)務線程池須實現(xiàn)EventExecutorGroup接口,4.1版本則可以直接使用JAVA自帶的線程池。

為了幫助用戶快速構建基于Netty的服務,Netty提供了兩個啟動器ServerBootstrapBootstrap,分別用于啟動服務器端和客戶端程序。group(EventLoopGroup...)方法用于指定一個或兩個Reactor,本例中指定為兩個。channel(Channel)方法本質(zhì)用來指定一個Channel工廠,本例中該工廠生產(chǎn)服務端用于accept客戶端連接的Channel,將默認使用Channel的無參構造方法。如果用戶需要自定義有參數(shù)的Channel,可自定義所需的工廠實現(xiàn)。option(Key, Value)用于指定TCP相關的參數(shù)以及一些Netty自定義的參數(shù)。childHandler()用于指定subReactor中的處理器,類似的,handler()用于指定mainReactor的處理器,只是默認情況下mainReactor中已經(jīng)添加了acceptor處理器,所以無需再指定。需要注意的是:這兩個方法并不能累積調(diào)用而達到增加多個處理器的目的,所以引入了 ChannelInitializer,它是一個特殊的Handler,功能是初始化多個Handler,如本例中的DecoderHandler,ComputeHandler,EncoderHandler。完成初始化工作后,ChannelInitializer會從Handler鏈中刪除。至此,如圖所示的框架已經(jīng)構建完畢。

最后臨門一腳,bind(int)方法將服務端Channel綁定到本地端口,成功后將accept客戶端的連接,從而是整個框架運行起來。使用sync()方法是由于Netty中的事件都是異步的,所以需要同步等待結果。準確的說,這個方法在這里使用是有問題的,sync()完成后只能表明綁定事件運行完畢,但并不能說明綁定成功,雖然失敗的可能性微乎其微。

f.channel().closeFuture().sync()方法僅僅是為了使當前main線程阻塞而不立即執(zhí)行之后的各種shutdown()方法,其語義是等到服務端接受客戶端連接的Channel被關閉時,才執(zhí)行后面代碼的操作。在實際應用中,這樣的代碼并不實用,我們可能需要接受諸如kill命令后,優(yōu)雅關閉線程組。

一些情況下,我們并不使用如圖所示的結構,比如當業(yè)務邏輯都很簡單,也就是如圖所示的decode,compute,encode能在短時間完成(數(shù)十毫秒或更少),那么可以不使用業(yè)務線程池。代碼也很簡單,只需要改動ChannelInitializer即可:

    b.childHandler(new ChannelInitializer<SocketChannel>() {
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
             ChannelPipeline p = ch.pipeline();
             p.addLast(new DecoderHandler());   // 解碼處理器
             p.addLast(new ComputeHandler());   // 計算處理器
             p.addLast(new EncoderHandler());   // 編碼處理器
         }
    });

事實上這是Netty的默認方法,也就是說不在addLast(Handler)方法中指定線程池,那么將使用默認的subReacor即woker線程池也即IO線程池執(zhí)行處理器中的業(yè)務邏輯代碼。

又比如,如開始的例子只讓IO線程池處理read,write等IO事件會覺得有點大材小用,于是將decode和encode交給IO線程處理,如果此時的compute查詢需要數(shù)據(jù)庫中的數(shù)據(jù),那么代碼可改動為如下:

    b.childHandler(new ChannelInitializer<SocketChannel>() {
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
             ChannelPipeline p = ch.pipeline();
             p.addLast(new DecoderHandler());   // 解碼處理器
             p.addLast(new EncoderHandler());   // 編碼處理器
             p.addLast(threadPool, new ComputeWithSqlHandler());   // 附帶SQL查詢的計算
         }
    });

最佳實踐
簡單快速的業(yè)務邏輯可由IO線程池執(zhí)行,復雜耗時的業(yè)務(如查詢數(shù)據(jù)庫,取得網(wǎng)絡連接等)使用新的業(yè)務邏輯線程池執(zhí)行。

本文介紹了Bootstrap的使用,如果還想知道背后的原理,可移步后續(xù)文章:自頂向下深入分析Netty(三)--Bootstrap源碼分析

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容