在(一)中叙述了ServerBootStarp.bind()方法的initAndRegister()
本文将讲述在AbstractBootstarp.doBind()中的第二个核心方法 doBind0()
AbstractBootstrap.doBind0()
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// channel = NioServerSokcetChannel
1、ChannelFuture接口。因为在Netty中所有的I/O操作都是异步的,所以在我们注册NIOServerSocketChannel的时候我们不必要等待是否注册成功再执行下一步的操作,这是Netty通过这个对象(异步操作对象),可以在NIOServerSocketChannel是否注册成功后返回给我们信息。以方便我们在以后调用。这里有四种状态
+---------------------------+
* | Completed successfully |
* +---------------------------+
* +----> isDone() = true |
* +--------------------------+ | | isSuccess() = true |
* | Uncompleted | | +===========================+
* +--------------------------+ | | Completed with failure |
* | isDone() = false | | +---------------------------+
* | isSuccess() = false |----+----> isDone() = true |
* | isCancelled() = false | | | cause() = non-null |
* | cause() = null | | +===========================+
* +--------------------------+ | | Completed by cancellation |
* | +---------------------------+
* +----> isDone() = true |
* | isCancelled() = true |
* +---------------------------+
这里调用的与channel相关联的eventLoop中的线程池提交绑定端口的任务。接下来就是执行runAllTasks中的任务。
第二个任务就是执行为channel绑定端口号了。
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress,promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
channel.bind()
首先执行AbstractChannel的bind
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
执行DefaultChannelPipeline的bind 这里是执行咱们在初始化DefaultChannelPipeline 时 自初始化的尾节点的bind()
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
执行 AbstractChannelHandlerContext.bind()
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
// 在DefaultChannelPipeline 中查找Netty初始化的执行绑定端口操作的handler
// ServerBootstrap$ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter
final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null);
}
return promise;
}
AbstractChannelHandlerContext.invokeBind()
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
bind(localAddress, promise);
}
}
DefaultChannelPipeline.bind()
/**
这里的unsafe 就是我们前面初始化的NioMessageUnsafe对象
*/
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
// 这里是调用NioMessageUnsafe的父类AbstractUnsafe的bind方法
unsafe.bind(localAddress, promise);
}
AbstractChannel.AbstractUnsafe.bind()
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
boolean wasActive = isActive();
可以看到,这里最终的方法就是 doBind 方法,执行成功后,执行通道的 fireChannelActive 的 方法,告诉所有的 handler ,已经成功绑定。
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
NioServerSocketChannel.doBind()
最终 doBind 就会追踪到 NioServerSocketChannel 的 doBind, 说明 Netty 底层使用的是 Nio
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
回到 bind 方法(alt+v),最后一步:safeSetSuccess(promise),告诉 promise 任务成功了。其可以执行监听器的
方法了。到此整个启动过程已经结束了。然后就是继续循环执行runAllTasks中的任务了。
Netty启动过程整理
- 创建两个NioEventLoopGroup 线程池。默认大小为CPU核数*2
- ServerBootStarp 的 父类 AbstractBootstarp 将group属性设置为BossGroup。ServerBootStarp 将childGroup 属性设置为 WorkerGroup。
- 通过bind()方法启动,内部重要方法initAndRegister() 和 doBind()方法
- initAndRegister 方法会创建反射NIOServerSocketChannel 及其相关的NIO的对象,pipeline(DefauleChannelPipeline) unsafe(NioMessageUnsafe) 同时也为pipeline 创建head 和 tail节点。
- doBind() 方法中调用 doBind0() 方法,该方法会 调用 NioServerSocketChannel 的 doBind() 方法对 JDK 的 channel 和端口进行绑定,完成 Netty 服务器的所有启动,并开始监听连接事件.