Netty簡介

Netty是什麼?

Netty 是一款異步的事件驅動的網絡應用程序框架,可讓我們更
快速地開發可維護的、高性能的面向協議的服務器和客戶端。

阻塞式IO與非阻塞式IO

  • 阻塞I/O: 比較消耗資源,僅適合中小數量的concurrent數量,一個Socket連線I/O就需要一個thread負責處理。
  • 非阻塞式IO: java.nio.channels.Selector 是
    Java 的非阻塞I/O 實現的關鍵。它使用了事件通知API
    以確定在一組非阻塞套接字中有哪些已經就緒能夠進
    行I/O 相關的操作。因為可以在任何的時間檢查任意
    的讀操作或者寫操作的完成狀態,如下圖所示,
    單一的thread便可以處理多個並行的連接。
    NIO API Struc

    參考自:Netty實戰

重要概念

Netty的線程機制

底下透過展示一段程式碼後,在個別去對每個重要的部分做分述、解釋

=================主要的服務啟動器
ServerBootStrap serverBS = new ServerBootStrap();

=================設置線程池(EventLoopGroup)
//BOSS線程池
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);

//WORK線程池:這樣的申明方式,主要是為了展示說明Netty的線程是怎樣工作的
ThreadFactory threadFactory = new DefaultThreadFactory("work thread pool");
//CPU個數
int processorsNumber = Runtime.getRuntime().availableProcessors();

EventLoopGroup workLoopGroup = new NioEventLoopGroup(processorsNumber * 2, threadFactory, 
SelectorProvider.provider());

//指定Netty的Boss線程和work線程
serverBS.group(bossLoopGroup , workLoogGroup);

//單存一點使用一個線程池來處理也可以
serverBS.group(workLoogGroup);

=================設置服務的通道類型
//只能是實現了ServerChannel接口的“服務器”通道類別
serverBS.channel(NioServerSocketChannel.class);


=================設置處理器(ChannelHandler)
//Netty的特色就在這一連串“通道水管”中的Handler
serverBS.childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel ch) throws Exception {
        ch.pipeline().addLast(new ByteArrayEncoder());
        ch.pipeline().addLast(new TCPServerHandler());
        ch.pipeline().addLast(new ByteArrayDecoder());
    }
});

=================設置netty服務器綁定的ip和端口
serverBS.option(ChannelOption.SO_BACKLOG, 128);
serverBS.childOption(ChannelOption.SO_KEEPALIVE, true);

serverBS.bind(new InetSocketAddress("0.0.0.0", 83));
//還可以監控多個端口
serverBS.bind(new InetSocketAddress("0.0.0.0", 84));

上面的Boss線程池,裡面只有一個線程(boss EventLoop),專門替ServerChannel工作,負責為傳入的連接創建一個Channel,同時為新Channel註冊所感興趣事件的處理器; work線程池會為這個新的Channel分配一個work線程(work EventLoop),其工作就是負責去監看OS是不是有他感興趣的IO事件發生(例如SocketChannel的READ事件),若有則調用相對應的ChannelHandler去處理。

當某個channel失效後(例如在ChannelHandler方法中調用channelHandlerContext.close())這個channel將從綁定的EventLoop中被剔除。

而這邊提到的EventLoop(NioEventLoop),其內部實際上就是封裝了前面所提到的Java NIO框架的Selector,其按照底層JAVA NIO的Selector的事件狀態,決定執行ChannelHandler中的哪一個事件方法(Netty中的事件,包括了channelRegistered、channelUnregistered、channelActive、channelInactive、channelRead等事件方法)。執行完成後,work線程將一直輪詢直到操作系統回覆下一個它所管理的channel發生了新的IO事件。
NIO eventLoopExplain
PS:這個圖可以說明具有兩個EventLoopGroup的運作架構,左邊的boss EventLoop 負責接收新連接,右邊work EventLoop綁定給新連接的Channel(參考自:Netty實戰)

ByteBuf

Channel

  • Netty中的Channel專門代表網絡通信,它是由客戶端地址 + 服務器地址 + 網絡操作狀態 構成的。
  • 在Netty中,不止封裝了JAVA NIO的IO模型,還封裝了JAVA BIO的阻塞同步IO通信模型(Socket Class)。將他們在表現上都抽象成Channel,大大降低直接操作Socket的複雜度。
  • 是一個更豐富、抽象的通道,如下圖:
    NIO Channel

ChannelPipeline、ChannelHandler

  • Netty中的每一個Channel,都有一個獨立的ChannelPipeline,中文稱為“通道水管”。只不過這個水管是雙向的裏面流淌著數據,數據可以通過這個“水管”流入到服務器,也可以通過這個“水管”從服務器流出。

  • 在ChannelPipeline中,會有一組處理器。我們稱之為“ChannelHandler”(處理器或者過濾器)。同“流入”和“流出”的概念相對應:用於處理/過濾 流入數據的ChannelHandler,稱之為“ChannelInboundHandler”;用於處理/過濾 流出數據的ChannelHandler,稱之為“ChannelOutboundHandler”

  • 數據在ChannelPipeline中有一個一個的Handler進行處理,並形成一個新的數據狀態。這是典型的“責任鏈”模式。

  • ChannelInboundHandler:擴展此接口要實作11個方法,因此一般都擴展ChannelInboundHandlerAdapter,一般情況下只需覆寫自己關心的事件,不須要所有方法都實現。

  • ChannelOutboundHnadler:同樣可以擴展ChannelOutboundHandlerAdapter。

  • Channel的生命周期
    NIO ChannelEventCycle
    這個生命周期的事件方法調用順序只是針對Netty封裝使用JAVA NIO框架時,並且在進行TCP/IP協議監聽時的事件方法調用順序。

#
初次接觸Netty,這篇是將參考的電子書與覺得解釋的不錯的網路文章做了一些整理,算是讓看倌有個初略的概念,若有興趣深入,建議可以看看 Netty實戰這本書。

參考文章&圖片&文獻