第13章 服务端创建
13.1 原生NIO类库复杂性
开发高质量的NIO的程序并不简单,成本太高
13.2 服务端创建源码
通过ServerBootStrap启动辅助类启动Netty
13.2.1 创建时序图
以下是关键步骤:
-
创建ServerBootStrap实例。这是启动辅助类,提供一系列方法用于设置启动相关的参数,因为参数太多,所以构造函数没有参数
-
设置并绑定Reactor线程池,Netty的Reactor线程池是EventLoopGroup,就是EventGroup的数组。EventLoop就是用来处理所有注册到Selector上的Channel,由EventLoop的run方法进行轮询操作。除了处理I/O事件,也处理用户自定义的Task,和定时任务。
-
设置并绑定服务端Channel,Netty对NIO中的ServerSocketChannel进行了封装,对应NioServerSocketChannel。在ServerBootStrap中可以指定ServerSocketChannel的类型。
-
链路建立的时候创建并初始化ChannelPipeline,本质是一个负责处理网络事件的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转,由ChannelPipeline根据ChannelHandler的执行策略调度ChannelHandler,有一些典型的网络事件:
- 链路注册
- 链路激活
- 链路断开
- 接收到请求消息
- 请求消息接收并处理完毕
- 发送应答消息
- 链路发生异常
- 发生用户自定义事件
-
初始化ChannelPipeline后,添加并设置ChannelHandler。通过ChannelHandler可以完成用户自己的定制与扩展,同时Netty也提供了大量的系统ChannelHandler,一些实用的:
- 系统编解码框架-ByteToMessageCodec
- 通用基于长度的半包解码器-LengthFieldBasedFrameDecoder
- 码流日志打印Handler-LoggingHandler
- SSL安全认证Handler—SslHandler
- 链路空闲检测Handler-IdleStateHandler
- 流量整形Handler-ChannelTrafficShapingHandler
- Base64编解码-Base64Decoder和Base64Encoder
创建和添加ChannelHandler源码:
.childHandler(new ChannelInitializer<SocketChannel>() { // (4) @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( //new LoggingHandler(LogLevel.INFO), new EchoServerHandler()); } });
-
绑定并启动端口。将ServerSocketChannel注册到Selector上监听客户端连接
-
Selector轮询。由Reactor线程NioEventLoop负责调度和执行Selector轮询操作,选择准备就绪的Channel集合
-
当轮询到准备就绪的Channel之后,就由Reactor线程NioEventLoop执行ChannelPipeline的相应方法,最终执行ChannelHandler
-
执行ChannelHandler,ChannelPipeline根据具体网络事件的类型,调度并执行ChannelHandler
13.2.2 服务端创建源码
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void run() throws Exception {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
// (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
//new LoggingHandler(LogLevel.INFO),
new EchoServerHandler());
}
});
// Start the server.
ChannelFuture f = b.bind(port).sync(); // (5)
// 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