Netty學習筆記

何為Netty


  Netty是一個非同步事件驅動的網路應用程式框架,用於快速開發可維護的高性能協議伺服器和客戶端。

何為事件驅動


事件處理的兩道工序:

方法 說明
事件分離器(Event Demultiplexer) 將事件源(socket/file)的I/O時間分離出來(IO就緒事件,IO完成事件),並分傳遞到對應的I/O事件處理器。
事件處理器(Event Handler) 應用預先註冊需要處理的事件及其事件處理器(回調函數)。

事件分離器的兩種模式:

模式 說明
Reactor 事件分離器負責等待文件描述符或socket為讀寫操作準備就緒,然後將就緒事件傳遞給對應的處理器,最後由處理器負責完成實際的讀寫工作。I/O操作由應用完成,調用事件處理器時,表示I/O就緒。
Proactor demultiplexor事件處理器負責發起非同步I/O操作,將用戶定義的數據緩衝區地址和數據大小傳遞給作業系統,作業系統進行I/O操作,事件分離器捕獲IO操作完成事件,然後將事件傳遞給對應處理器。I/O操作由系統完成,調用事件處理器時,表示I/O完成。

何為非同步I/O


  常見的I/O模式有一下幾種,Netty是多路復用模式。假設場景:用戶想從水管管道取5000L水,但是這個水管的水流是斷斷續續的,那麼他有以下幾種方法:

模式 大白話 說明
阻塞 I/O(blocking IO) 他打開水龍頭,在必須原地等待水量達到5000L,然後這個任務才完成。 阻塞,同步I/O
非阻塞 I/O(nonblocking IO) 他打開水龍頭,然後去做別的事情,兩分鐘過來查看一次(輪詢),直到水位達到500L,然後這個任務才完成。 非阻塞,同步I/O
I/O 多路復用( IO multiplexing)(事件驅動I/O) 他加裝多個水龍頭,然後在水龍頭的前端安裝水滴感測器(阻塞),他在後台輪詢各個感測器的數據,一旦得到A水龍頭的水滴感測器已經被觸發時,他就打開水龍頭A接水,有一下幾種機制select,epoll,iocp,kqueue,poll。 非阻塞,同步I/O
訊號驅動 I/O( signal driven IO) 他加裝多個水龍頭,然後在水龍頭的前端安裝水滴感測器,當水龍頭A的水滴感測器觸發時,主動通知他,他就打開水龍頭A接水。 非阻塞,同步I/O
非同步 I/O(asynchronous IO) 他加裝多個水龍頭,然後在水龍頭的前端安裝水量感測器,並且將水龍頭打開,然後他就去做其他的事情了,當水來時,水量感測器統計已取的水量,達到5000L時,將多個水桶的水匯聚到一個桶里,讓他直接使用。 非阻塞,非同步I/O

I/O多路復用模式


模式 方法 說明 最大連接數 效率 消息傳遞方式
select 輪詢 同步非阻塞 FD_SETSIZE宏定義 FD的增加會造成線性遍歷速度慢的「線性下降性能問題」 內核需要將消息傳遞到用戶空間,都需要內核拷貝動作
kQueue 回調 同步非阻塞 與epoll相似 與epoll相似 與epoll相似
iocp 通知 非同步 待了解 待了解 待了解
poll 輪詢 同步非阻塞 基於鏈表來存儲沒有最大連接數的限制 與select一樣 與select一樣
ePoll 回調 同步非阻塞 連接數有上限,效率與連接數成反比 socket活躍大會有性能問題 通過內核和用戶空間共享一塊記憶體來實現

Netty示例解析


以下是一個經典示例:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.mqtt.MqttDecoder;
import io.netty.handler.codec.mqtt.MqttEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.yb.iot.mqtt.handler.ChannelPipelineHandler;
import org.yb.iot.mqtt.handler.InboundMessageHandler;

import java.util.concurrent.TimeUnit;

public class NettyServer {


    public static void main(String[] args) {
        /**
         * NioEventLoopGroup是處理I / O操作的多執行緒事件循環,多種實現可選。
         * 使用多少個執行緒以及如何將它們映射到創建的通道取決於EventLoopGroup實現,甚至可以通過構造函數進行配置。
         * 在上文中提到,I/O多路復用機制有多種,Netty中提供了多種實現,,以Group尾綴的為多執行緒實現。
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            /**
             * 設置伺服器的幫助程式類。
             */
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            /**
             * bossGroup通常稱為「老闆」,接受傳入的連接。
             * workerGroup通常稱為「工人」,一旦老闆接受連接並將註冊的連接註冊給工人,便處理已接受連接的流量。
             */
            serverBootstrap.group(bossGroup, workerGroup)
                    /**
                     * 指定IO模型
                     */
                    .channel(NioServerSocketChannel.class)

                    /**
                     * 配置自定義的事件處理器。
                     */
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Autowired
                        private InboundMessageHandler inboundMessageHandler;

                        /**
                         * 初始化channel模型的上下文
                         * @param socketChannel
                         * @throws Exception
                         */
                        @Override
                        public void initChannel(SocketChannel socketChannel) {
                            /**
                             * 事件處理器責任鏈,可以動態增刪
                             * 可以進行例如協議編碼解碼,心跳,內容長度,粘包分包處理,讀寫處理等,
                             * Netty已經默認提供了多種標準協議的解碼編碼實現,全都在io.netty.handler.codec.*下,如MQTT,HTTP等。
                             */
                            ChannelPipeline pipeline = socketChannel.pipeline();

                            /** 最大內容長度 */
                            pipeline.addLast(new HttpObjectAggregator(1024 * 1024 * 64));

                            /** 心跳時間 */
                            pipeline.addLast("idleStateHandler", new IdleStateHandler(120, 120, 120, TimeUnit.SECONDS));

                            /** 協議編碼 */
                            pipeline.addLast(MqttEncoder.INSTANCE);

                            /** 協議解碼 */
                            pipeline.addLast(new MqttDecoder(1024 * 1024 * 64));

                            /** 消息處理 */
                            pipeline.addLast(inboundMessageHandler);
                        }
                    })

                    /**
                     * 配置EventLoopGroup模型的參數
                     */
                    .option(ChannelOption.SO_BACKLOG, 128)

                    /**
                     * 配置Channel模型的參數
                     */
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            /**
             * 綁定並開始接受傳入的連接,ChannelFuture支援I/O完成事件監聽。
             */
            ChannelFuture channelFuture = serverBootstrap.bind(1883).sync();

            /**
             * 註冊等待Socket伺服器關閉。
             */
            channelFuture.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            /**
             * 優雅的關閉並退出服務
             */
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

EventLoopGroup與Channel的實現對應關係如下:

單執行緒模型 多執行緒模型/主從執行緒模型 通道模型 模式
EpollEventLoop.class EpollEventLoopGroup.class EpollServerSocketChannel.class epoll
KQueueEventLoop.class KQueueEventLoopGroup.class KQueueServerSocketChannel.class kQueue
NioEventLoop.class NioEventLoopGroup.class NioServerSocketChannel.class select
AioEventLoop.class AioEventLoopGroup.class AioServerSocketChannel.class iocp

Netty重要組件:

類 說明
Channel 管道對象
ChannelPipeline 管道事件處理器責任鏈
ChannelHandler 管道事件處理器的介面
ChannelHandlerContext 管道事件處理器的上下文
ChannelFuture 管道非同步操作的結果
ByteBuf 管道一幀數據的對象,位元組容器,通過引用計數的方式實現記憶體回收

參考文檔

Netty Wiki
Netty 實戰 中文版
Netty設計原理
並發編程網 Netty
linux下select/poll/epoll機制的比較
Linux IO模式及 select、poll、epoll詳解
Netty 系列之 Netty 執行緒模型
深入研究Netty之執行緒模型詳解
Netty學習之IO模型

Tags: