先上一段经典使用姿势的样例代码。
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, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
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 {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
在ServerBootstrap中配置了两组NioEventLoopGroup,如上,bossGroup、workerGroup。这就是netty中主从线程池模型的实现。
1、当服务端要监听一个端口号,以便处理连接该端口号的accept请求,这是需要有NioServerSocketChannel,而该channel是需要注册到eventloop上去的,这时就会从bossGroup中选择一个eventloop;
2、当一个客户端的连接被accept之后,这时,会从workerGroup中选择一个eventloop用来处理该SocketChannel上的所有操作。
另外在配置中,配置了两个handler,一个是通过handler接口配置,一个是通过childHandler配置,两个handler的用途是不一样的。
1、handler是NioServerSocketChannel中的ChannelPipeline中的一个环节;
2、childHandler则是客户端连接过来之后,新建的NioSocketChannel中的ChannelPipeline中的一个环节。
接下来还是先重点分析下bind过程,看如果建立服务端,主要逻辑在doBind方法中,代码如下
private ChannelFuture doBind(final SocketAddress localAddress) {
// 1
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
// 2
final ChannelPromise promise;
if (regFuture.isDone()) {
promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
promise = new DefaultChannelPromise(channel, GlobalEventExecutor.IN