<h2>服務(wù)端</h2>
以下是Netty官方的一個(gè)Echo服務(wù)示例:
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();
}
}
}
上面代碼創(chuàng)建Server端服務(wù)的流程如下:
- 創(chuàng)建ServerBootstrap實(shí)例
- 設(shè)置EventLoopGroup
- 設(shè)置創(chuàng)建的Channel類型
- option配置屬性
- 設(shè)置Handler,處理請(qǐng)求
- 設(shè)置ChildHandler,處理對(duì)應(yīng)channel的請(qǐng)求
- 通過(guò)bind創(chuàng)建Chnnel并綁定,啟動(dòng)服務(wù)
服務(wù)端創(chuàng)建時(shí)序圖

服務(wù)端重要組件
ServerBootstrap
ServerBootstrap是Netty服務(wù)端的啟動(dòng)輔助類,提供一系列的方法用于設(shè)置服務(wù)端的參數(shù)和配置,簡(jiǎn)化開(kāi)發(fā)。(衍生一點(diǎn):ServerBootstrap的構(gòu)造方法是無(wú)參的,因?yàn)閰?shù)太多所以采用了Builder模式)
繼承自AbstractBootstrap,核心屬性有childGroup和childHandler。
- childGroup:負(fù)責(zé)調(diào)度和執(zhí)行客戶端的接入、網(wǎng)絡(luò)讀寫(xiě)事件的處理、用戶自定義任務(wù)和定時(shí)任務(wù)的執(zhí)行
- childHandler:自定義的業(yè)務(wù)Handler
AbstractBootstrap核心屬性有g(shù)roup和handler。
- group:處理客戶端的鏈接請(qǐng)求,并轉(zhuǎn)交給childGroup(讀取的數(shù)據(jù)是穿件的NioSocketChannel)
Reactor線程池
Netty的Reactor線程池是EventLoopGroup,實(shí)際上是一個(gè)EventLoop的數(shù)組。
EventLoop的職責(zé)是處理所有注冊(cè)到本線程多路復(fù)用器Selector上的Channel,Selector的輪訓(xùn)操作有EventLoop線程run方法驅(qū)動(dòng)。
另外用戶自定義的Task和定時(shí)任務(wù)Task也由統(tǒng)一的EventLoop負(fù)責(zé)處理。
Channel
作為Nio服務(wù),需要?jiǎng)?chuàng)建ServerSocketChannel,Netty對(duì)原生NIO類庫(kù)做了封裝,對(duì)應(yīng)實(shí)現(xiàn)類為NioServerSocketChannel。
用戶只需要制定Channel的實(shí)現(xiàn)類型,內(nèi)部通過(guò)反射機(jī)制來(lái)創(chuàng)建對(duì)應(yīng)的實(shí)例。
因?yàn)橹辉诒O(jiān)聽(tīng)端口時(shí)創(chuàng)建,所以反射的性能影響并不大。
ChannelPipeline
ChannelPipeline是網(wǎng)絡(luò)事件處理的職責(zé)鏈,負(fù)責(zé)管理和執(zhí)行ChannelHandler。網(wǎng)絡(luò)事件以事件流的形式在ChannelPipeline中流轉(zhuǎn)。
ChannelHandler
ChannelHandler是提供給用戶定制和擴(kuò)展的關(guān)鍵接口,包括編解碼,業(yè)務(wù)處理等都是通過(guò)ChannelHandler進(jìn)行的。
Selector
Selector輪訓(xùn)操作由NioEventLoop調(diào)度和執(zhí)行,選擇準(zhǔn)備就緒的Channel集合。
NioServerSocketChannel
綁定Server端地址的Server,讀取客戶端的鏈接請(qǐng)求(只有一個(gè),在bind時(shí)創(chuàng)建)。
NioSocketChannel
和客戶端之間的鏈接。
服務(wù)端線程模型

- mainReactor:parentGroup
- subReactor:childGroup
- ThreadPool:如果沒(méi)指定,使用childGroup執(zhí)行,如果指定了則是業(yè)務(wù)線程(執(zhí)行業(yè)務(wù)Handler的線程)
Handler模型

Server啟動(dòng)的關(guān)鍵流程
- bind操作創(chuàng)建了NioServerSocketChannel并注冊(cè)到NioEventLoop中(parent group中會(huì)有一個(gè)線程執(zhí)行selector的輪訓(xùn)操作)
Server端的請(qǐng)求接入流程
- NioEventLoop輪訓(xùn)到到就緒時(shí)間后,調(diào)用Unsafe.read(NioMessageUnsafe實(shí)現(xiàn))創(chuàng)建NioSocketChannel并傳遞到ServerBootstrap的ServerBootstrapAcceptor的channelRead方法中。
- ServerBootstrapAcceptor的channelRead方法將NioSocketChannel注冊(cè)到childGroup的Selector上(實(shí)現(xiàn)代碼是AbstractNioSocket的doRegister;之后NioSocketChannel的事件就由child group處理,這點(diǎn)和NioServerSocketChannel的注冊(cè)、處理是一樣的;和上面的線程模型也是呼應(yīng)的:兩個(gè)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();
}
}
創(chuàng)建客戶端的大致流程:
- 創(chuàng)建Bootstrap實(shí)例
- 設(shè)置EventLoop
- 指定Channel類型
- option配置
- 指定Handler
- connect
客戶端創(chuàng)建時(shí)序圖

客戶端和服務(wù)端的模式基本一致,由線程輪訓(xùn)Selector的事件,由Pipeline進(jìn)行事件傳遞,EventLoop進(jìn)行處理。