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 |
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模型

