<h2>服務端</h2>
以下是Netty官方的一個Echo服務示例:
public final class EchoServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
上面代碼創建Server端服務的流程如下:
- 創建ServerBootstrap實例
- 設置EventLoopGroup
- 設置創建的Channel類型
- option配置屬性
- 設置Handler,處理請求
- 設置ChildHandler,處理對應channel的請求
- 通過bind創建Chnnel并綁定,啟動服務
服務端創建時序圖
服務端重要組件
ServerBootstrap
ServerBootstrap是Netty服務端的啟動輔助類,提供一系列的方法用于設置服務端的參數和配置,簡化開發。(衍生一點:ServerBootstrap的構造方法是無參的,因為參數太多所以采用了Builder模式)
繼承自AbstractBootstrap,核心屬性有childGroup和childHandler。
- childGroup:負責調度和執行客戶端的接入、網絡讀寫事件的處理、用戶自定義任務和定時任務的執行
- childHandler:自定義的業務Handler
AbstractBootstrap核心屬性有group和handler。
- group:處理客戶端的鏈接請求,并轉交給childGroup(讀取的數據是穿件的NioSocketChannel)
Reactor線程池
Netty的Reactor線程池是EventLoopGroup,實際上是一個EventLoop的數組。
EventLoop的職責是處理所有注冊到本線程多路復用器Selector上的Channel,Selector的輪訓操作有EventLoop線程run方法驅動。
另外用戶自定義的Task和定時任務Task也由統一的EventLoop負責處理。
Channel
作為Nio服務,需要創建ServerSocketChannel,Netty對原生NIO類庫做了封裝,對應實現類為NioServerSocketChannel。
用戶只需要制定Channel的實現類型,內部通過反射機制來創建對應的實例。
因為只在監聽端口時創建,所以反射的性能影響并不大。
ChannelPipeline
ChannelPipeline是網絡事件處理的職責鏈,負責管理和執行ChannelHandler。網絡事件以事件流的形式在ChannelPipeline中流轉。
ChannelHandler
ChannelHandler是提供給用戶定制和擴展的關鍵接口,包括編解碼,業務處理等都是通過ChannelHandler進行的。
Selector
Selector輪訓操作由NioEventLoop調度和執行,選擇準備就緒的Channel集合。
NioServerSocketChannel
綁定Server端地址的Server,讀取客戶端的鏈接請求(只有一個,在bind時創建)。
NioSocketChannel
和客戶端之間的鏈接。
服務端線程模型
- mainReactor:parentGroup
- subReactor:childGroup
- ThreadPool:如果沒指定,使用childGroup執行,如果指定了則是業務線程(執行業務Handler的線程)
Handler模型
Server啟動的關鍵流程
- bind操作創建了NioServerSocketChannel并注冊到NioEventLoop中(parent group中會有一個線程執行selector的輪訓操作)
Server端的請求接入流程
- NioEventLoop輪訓到到就緒時間后,調用Unsafe.read(NioMessageUnsafe實現)創建NioSocketChannel并傳遞到ServerBootstrap的ServerBootstrapAcceptor的channelRead方法中。
- ServerBootstrapAcceptor的channelRead方法將NioSocketChannel注冊到childGroup的Selector上(實現代碼是AbstractNioSocket的doRegister;之后NioSocketChannel的事件就由child group處理,這點和NioServerSocketChannel的注冊、處理是一樣的;和上面的線程模型也是呼應的:兩個reactor)。
<h2>客戶端</h2>
public static void main(String[] args) throws Exception {
// Configure SSL.git
final SslContext sslCtx;
if (SSL) {
sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
} else {
sslCtx = null;
}
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoClientHandler());
}
});
// Start the client.
ChannelFuture f = b.connect(HOST, PORT).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
}
創建客戶端的大致流程:
- 創建Bootstrap實例
- 設置EventLoop
- 指定Channel類型
- option配置
- 指定Handler
- connect
客戶端創建時序圖
客戶端和服務端的模式基本一致,由線程輪訓Selector的事件,由Pipeline進行事件傳遞,EventLoop進行處理。