面試官:說說Netty核心組件?
Netty 核心組件是指 Netty 在執(zhí)行過程中所涉及到的重要概念,這些核心組件共同組成了 Netty 框架,使 Netty 框架能夠正常的運行。
Netty 核心組件包含以下內(nèi)容:
- 啟動器 Bootstrap/ServerBootstrap
- 事件循環(huán)器 EventLoopGroup/EventLoop
- 通道 Channel
- 通道處理器 ChannelHandler
- 通道管道 ChannelPipeline
這些組件的交互流程如下:
上圖是 Netty 邏輯處理架構(gòu),這個邏輯處理架構(gòu)為典型網(wǎng)絡分層架構(gòu)設計,共分為網(wǎng)絡通信層、事件調(diào)度層、服務編排層,每一層各司其職,共同成為了 Netty 的核心組件。
1.Bootstrap/ServerBootstrap【啟動器】
Bootstrap 是“引導”的意思,它主要負責整個 Netty 程序的啟動、初始化、服務器連接等過程,它相當于一條主線,串聯(lián)了 Netty 的其他核心組件。
“
PS:Netty 中的引導器共分為兩種類型:一個為用于客戶端引導的 Bootstrap,另一個為用于服務端引導的 ServerBootStrap。
2.Channel【通道】
Channel 是網(wǎng)絡數(shù)據(jù)的傳輸通道,它代表了到實體(如硬件設備、文件、網(wǎng)絡套接字或能夠執(zhí)行 I/O 操作的程序組件)的開放連接,如讀操作和寫操作。
Channel 提供了基本的 API 用于網(wǎng)絡 I/O 操作,如 register、bind、connect、read、write、flush 等。Netty 自己實現(xiàn)的 Channel 是以 JDK NIO Channel 為基礎的,相比較于 JDK NIO,Netty 的 Channel 提供了更高層次的抽象,同時屏蔽了底層 Socket 的復雜性,賦予了 Channel 更加強大的功能,你在使用 Netty 時基本不需要再與 Java Socket 類直接打交道。
常見的 Channel 類型有以下幾個:
- NioServerSocketChannel 異步 TCP 服務端。
- NioSocketChannel 異步 TCP 客戶端。
- OioServerSocketChannel 同步 TCP 服務端。
- OioSocketChannel 同步 TCP 客戶端。
- NioDatagramChannel 異步 UDP 連接。
- OioDatagramChannel 同步 UDP 連接。
當然 Channel 也會有多種狀態(tài),如連接建立、連接注冊、數(shù)據(jù)讀寫、連接銷毀等狀態(tài)。
3.EventLoopGroup/EventLoop【事件循環(huán)器】
EventLoopGroup 是一個處理 I/O 操作和任務的線程組。在 Netty 中,EventLoopGroup 負責接受客戶端的連接,以及處理網(wǎng)絡事件,如讀/寫事件。它包含多個 EventLoop,每個 EventLoop 包含一個 Selector 和一個重要的組件,用于處理注冊到其上的 Channel 的所有 I/O 事件
(1)EventLoopGroup、EventLoop和Channel
它們?nèi)叩年P系如下:
- 一個 EventLoopGroup 往往包含一個或者多個 EventLoop。EventLoop 用于處理 Channel 生命周期內(nèi)的所有 I/O 事件,如 accept、connect、read、write 等 I/O 事件。
- EventLoop 同一時間會與一個線程綁定,每個 EventLoop 負責處理多個 Channel。
- 每新建一個 Channel,EventLoopGroup 會選擇一個 EventLoop 與其綁定。該 Channel 在生命周期內(nèi)都可以對 EventLoop 進行多次綁定和解綁。
(2)線程模型
Netty 通過創(chuàng)建不同的 EventLoopGroup 參數(shù)配置,就可以支持 Reactor 的三種線程模型:
- 單線程模型:EventLoopGroup 只包含一個 EventLoop,Boss 和 Worker 使用同一個EventLoopGroup;
- 多線程模型:EventLoopGroup 包含多個 EventLoop,Boss 和 Worker 使用同一個EventLoopGroup;
- 主從多線程模型:EventLoopGroup 包含多個 EventLoop,Boss 是主 Reactor,Worker 是從 Reactor,它們分別使用不同的 EventLoopGroup,主 Reactor 負責新的網(wǎng)絡連接 Channel 創(chuàng)建,然后把 Channel 注冊到從 Reactor。
4.ChannelHandler【通道處理器】
ChannelHandler 是 Netty 處理 I/O 事件或攔截 I/O 操作的組件。當發(fā)生某種 I/O 事件時(如數(shù)據(jù)接收、連接打開、連接關閉等),ChannelHandler 會被調(diào)用并處理這個事件。
例如,數(shù)據(jù)的編解碼工作以及其他轉(zhuǎn)換工作實際都是通過 ChannelHandler 處理的。站在開發(fā)者的角度,最需要關注的就是 ChannelHandler,我們很少會直接操作 Channel,都是通過 ChannelHandler 間接完成。
5.ChannelPipeline【通道管道】
ChannelPipeline 是 ChannelHandler 的容器,提供了一種方式,以鏈式的方式組織和處理跨多個 ChannelHandler 之間的交互邏輯。當數(shù)據(jù)在管道中流動時,它會按照 ChannelHandler 的順序被處理。
6.Netty 簡單示例
下面是一個使用 Netty 構(gòu)建的最簡單服務器端和客戶端示例,這個例子中,服務器接收到客戶端的消息后,會直接將消息原樣回傳給客戶端。
(1)服務器端
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class NettyServer {
public static void main(String[] args) throws Exception {
// 創(chuàng)建BossGroup和WorkerGroup,它們都是EventLoopGroup的實現(xiàn)
// BossGroup負責接收進來的連接
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// WorkerGroup負責處理已經(jīng)被接收的連接
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 創(chuàng)建服務器端的啟動對象,配置參數(shù)
ServerBootstrap bootstrap = new ServerBootstrap();
// 設置兩個線程組
bootstrap.group(bossGroup, workerGroup)
// 設置服務器通道實現(xiàn)類型
.channel(NioServerSocketChannel.class)
// 設置通道初始化器,主要用來配置管道中的處理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 向管道加入處理器
// 解碼器:ByteBuf -> String
ch.pipeline().addLast(new StringDecoder());
// 編碼器:String -> ByteBuf
ch.pipeline().addLast(new StringEncoder());
// 自定義的處理器
ch.pipeline().addLast(new ServerHandler());
}
});
System.out.println("服務器 is ready...");
// 綁定一個端口并且同步,生成了一個ChannelFuture對象
ChannelFuture cf = bootstrap.bind(6668).sync();
// 對關閉通道進行監(jiān)聽
cf.channel().closeFuture().sync();
} finally {
// 優(yōu)雅關閉線程組
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
(2)客戶端代碼
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class NettyClient {
public static void main(String[] args) throws Exception {
// 創(chuàng)建EventLoopGroup,相當于線程池
EventLoopGroup group = new NioEventLoopGroup();
try {
// 創(chuàng)建客戶端啟動對象
Bootstrap bootstrap = new Bootstrap();
// 設置相關參數(shù)
bootstrap.group(group) // 設置線程組
.channel(NioSocketChannel.class) // 設置客戶端通道實現(xiàn)類型
.handler(new ChannelInitializer<SocketChannel>() { // 設置處理器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 向管道加入處理器
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
// 自定義的處理器
ch.pipeline().addLast(new ClientHandler());
}
});
System.out.println("客戶端 is ready...");
// 發(fā)起異步連接操作
ChannelFuture future = bootstrap.connect("127.0.0.1", 6668).sync();
// 發(fā)送消息
future.channel().writeAndFlush("Hello Server!");
// 對關閉通道進行監(jiān)聽
future.channel().closeFuture().sync();
} finally {
group.shutdownGracefully(); // 優(yōu)雅關閉線程組
}
}
}
參考&鳴謝
《Netty核心原理剖析與RPC實踐》