服务端启动主要流程
•创建 selector
•创建 server socket channel
•初始化 server socket channel
•给 server socket channel 从 boss group 中选择一个 NioEventLoop
•将 server socket channel 注册到选择的 NioEventLoop 的 selector
•绑定地址启动
•注册接受连接事件(OP_ACCEPT)到 selector上
从Echo服务器示例入手
在《引导器作用:客户端和服务端启动都要做些什么?》的课程中,我们介绍了如何使用引导器搭建服务端的基本框架。在这里我们实现了一个最简单的 Echo 服务器,用于调试 Netty 服务端启动的源码。
public class EchoServer {
public void startEchoServer(int port) throws Exception {
//默认会创建 cpu核心数*2 个 NioEventLoop
//创建NioEventLoop 会调用openSelector 创建一个Selector
//将创建的Selector赋值给NioEventLoop的selector属性
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast
(new FixedLengthFrameDecoder(10));
ch.pipeline().addLast(new ResponseSampleEncoder());
ch.pipeline().addLast(new RequestSampleHandler());
}
});
//同步阻塞
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoServer().startEchoServer(8088);
}
}
public class ResponseSampleEncoder extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, ResponseSample msg, ByteBuf out) {
if (msg != null) {
out.writeBytes(msg.getCode().getBytes());
out.writeBytes(msg.getData().getBytes());
out.writeLong(msg.getTimestamp());
}
}
}
public class RequestSampleHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String data = ((ByteBuf) msg).toString(CharsetUtil.UTF_8);
ResponseSample response = new ResponseSample("OK", data,
System.currentTimeMillis());
ctx.channel().writeAndFlush(response);
}
}
我们以引导器 ServerBootstrap 为切入点,开始深入分析 Netty 服务端的启动流程。在服务端启动之前,需要配置 ServerBootstrap 的相关参数,这一步大致可以分为以下几个步骤:
配置 EventLoopGroup 线程组;
配置 Channel 的类型;
设置初始化Handler;
设置网络监听的端口;
设置处理Handler;
配置 Channel 参数。
配置 ServerBootstrap 参数的过程非常简单,把参数值保存在 ServerBootstrap 定义的成员变量里就可以了。我们可以看下 ServerBootstrap 的成员变量定义,基本与 ServerBootstrap 暴露出来的配置方法是一一对应的。如下所示,我以注释的形式说明每个成员变量对应的调用方法。
volatile EventLoopGroup group; // group()
volatile EventLoopGroup childGroup; // group()
volatile ChannelFactory, Object> childOptions = new ConcurrentHashMap, Object>[] currentChildOptions =
childOptions.entrySet().toArray(newOptionArray(0));
final Entry, Object>[] currentChildOptions =
childOptions.entrySet().toArray(newOptionArray(0));
final Entry