上一篇我们说到dobind作为一个task加入taskQueue等待SingleThreadventExecutor执行,那么它是如何触发的呢?bind准备工作对channelpipeline和channelHandler做了哪些,handler链含有哪些元素呢?channel进行bind过程中对handler的执行次序是什么呢?handler链在fireChannelActive中handler都做了哪些?本文一一进行揭示,有不当之处欢迎拍砖。
一、bind的触发
在上一篇《netty5.0之server端NioServerSocketChannel的init和register流程》的Fig1中我们看到有dobind方法,并留下疑问doBind在何时被调用呢。下面我们看Fig1.1,该图同时说明了前面我们提到的:SingleThreadventExecutor的run方法,NioEventLoop的run方法的作用通过runAllTasks就是运行taskQueue的每个任务,一个任务就是channel的bind。Fig1的line:247所在的类方法(operationComplete)会回调用上一篇Fig 1中的dobind方法。那么是在何处触发的呢。
Fig 1.1
在上一篇Fig 2中,eventloop执行的runnable中调用了了register0,即本篇中line:426。在AbstractChannel的这个方法做了什么呢?
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!ensureOpen(promise)) {
return;
}
doRegister();//tag1.1
registered = true;
promise.setSuccess();//tag1.2
pipeline.fireChannelRegistered();//tag1.3
if (isActive()) {
pipeline.fireChannelActive();
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
if (!promise.tryFailure(t)) {
logger.warn(
"Tried to fail the registration promise, but it is complete already. " +
"Swallowing the cause of the registration failure:", t);
}
}
}
tag1.1 中进行了selector注册@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}
tag 1.2
从Fig 1.2中我们可以看到setSuccess后是DefaultChannelPromise在做事,notify listeners进行调用。就是Fig1中的operationComplete。
Fig 1.2
二、bind的准备工作
Fig 3表明进入了doBind流程。
Fig 3
在AbstractBootStrap的doBind0方法需要学习两点:
1、把bind作为一个任务加入eventloop中共SingleThreadEventExecutor调用
2、run()中被执行,直到channelRegistered()被触发。
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
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());
}
}
});
}
下面就学习tag1.3
(这部分的学习参考:http://my.oschina.net/geecoodeer/blog/193237)
DefaultChannelPipeline
是ChannelPipeline
的实现类,DefaultChannelPipeline
内部维护了两个指针:final
DefaultChannelHandlerContext head; final DefaultChannelHandlerContext tail;
,分别指向链表的头部和尾部;而DefaultChannelHandlerContext
内部是一个链表结构:volatile
DefaultChannelHandlerContext next;volatile DefaultChannelHandlerContext prev;
,而每个DefaultChannelHandlerContext
与ChannelHandler
实例一一对应。
从上面可以看到,这是个经典的Intercepting
Filter模式实现。下面我们再接着从tag1.3代码看起,pipeline.fireChannelRegistered();
依次执行如下两个方法。此时handler链是HeadHandler,ServerBootstrap$1和TailHandler。
tag1.3.0:
DefaultChannelPipeline:
@Override
public ChannelPipeline fireChannelRegistered() {
head.fireChannelRegistered();
return this;
}
DefaultChannelHandlerContext:@Override
public ChannelHandlerContext fireChannelRegistered() {
DefaultChannelHandlerContext next = findContextInbound(MASK_CHANNEL_REGISTERED);
next.invoker.invokeChannelRegistered(next);
return this;
}
DefaultChannelHandlerInvoker:@Override
public void invokeChannelRegistered(final ChannelHandlerContext ctx) {
if (executor.inEventLoop()) {
invokeChannelRegisteredNow(ctx);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
invokeChannelRegisteredNow(ctx);
}
});
}
}
@Override
@SuppressWarnings("unchecked")
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ChannelPipeline pipeline = ctx.pipeline();
boolean success = false;
try {
initChannel((C) ctx.channel());//tag1.3.1
pipeline.remove(this);//tag1.3.2
ctx.fireChannelRegistered();//tag1.3.3
success = true;
} catch (Throwable t) {
logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
} finally {
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
if (!success) {
ctx.close();
}
}
}
tag1.3.1:由于ServerBootstrap(ChannelInitializer<C>)这个类继承了ChannelInitializer,所以会执行了ChannelInitializer.channelRegistered这个方法。下面的initChannel回调了initChannel
(回答了:上一篇文章ChannelInitializer的initChannel方式是在何处被调用。该方法把ServerBootstrapAcceptor这个Handler加入到Pipeline中;此时handler链情况如下:HeadHandler,ServerBootstrap$1, ServerBootstrap$ServerBootstrapAcceptor和TailHandler。
再回过头看看ServerBootStrap中的这块代码,其实进行了两个addlast。第一个add ServerBootstrap$1,第二个add ServerBootstrapAcceptor。
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ServerBootstrapAcceptor(currentChildHandler, currentChildOptions,
currentChildAttrs));
}
}
对于二次add,下面代码处debug见图@Override
public ChannelPipeline addLast(ChannelHandlerInvoker invoker, final String name, ChannelHandler handler) {
synchronized (this) {
checkDuplicateName(name);
DefaultChannelHandlerContext newCtx =
new DefaultChannelHandlerContext(this, invoker, name, handler);
addLast0(name, newCtx);
}
return this;
}
pipeline.remove(this);
又把ServerBootstrap$1这个Handler给删除了,从而完成初始化的效果。需要提醒的是,
ServerBootstrapAcceptor的currentChildHandler属性包含了在客户端代码注册的TelnetServerInitializer
类。
在tag1.3.3里,通过执行ctx.fireChannelRegistered();
又找到了下一个handler。
pipeline.fireChannelRegistered();
执行。此时满足regfuture.success=true,于是执行AbstractBootStrap的doBind0方法进行绑定。
三、bind之handler处理
netty把channel的bind作为一个task,下图的层层调用表明了bind的过程:
1、AbstractChannel使用私有成员pipeline进行bind;见line:189
2、DefaultChannelPipeline调用tailHandler进行bind;
3、DefaultChannelHandlerContext调用,期间findContextOutbound 使用pre找到HeadHandler。
4、一直到HeadHander调用bind。
AbstractChannel:
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
DefaultChannelPipeline:@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
DefaultChannelHandlerContext@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
DefaultChannelHandlerContext next = findContextOutbound(MASK_BIND);
next.invoker.invokeBind(next, localAddress, promise);
return promise;
}
DefaultChannelHandlerInvoker
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
DefaultChannelHandlerContext next = findContextOutbound(MASK_BIND);
next.invoker.invokeBind(next, localAddress, promise);
return promise;
}
ChannelHandlerInvokerUtil:public static void invokeBindNow(
final ChannelHandlerContext ctx, final SocketAddress localAddress, final ChannelPromise promise) {
try {
ctx.handler().bind(ctx, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
}
HeadHandler:@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
unsafe.bind(localAddress, promise);
}
四、bind之channel
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (!ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (!PlatformDependent.isWindows() && !PlatformDependent.isRoot() &&
Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
}
boolean wasActive = isActive();
try {
doBind(localAddress);//tag4.1
} catch (Throwable t) {
promise.setFailure(t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {//tag4.2
@Override
public void run() {
pipeline.fireChannelActive();//tag4.3
}
});
}
promise.setSuccess();//tag4.4
}
tag4.1执行真正的bind端口:NioSocketSeverChannel
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
tag4.2 执行如下方法,eventLoop().execute(task); `在后续分析。现在暂时忽略。
tag4.4
在 tag4.4.1设置了成功状态,然后该方法返回,继续执行了tag4.4.2方法。由于listeners为 null,所以直接返回。此时执行tag4.3的代码
@Override
public ChannelPromise setSuccess() {
return setSuccess(null);
}
@Override
public ChannelPromise setSuccess(Void result) {
super.setSuccess(result);
return this;
}
@Override
public Promise<V> setSuccess(V result) {
if (setSuccess0(result)) {
notifyListeners();//tag4.4.2
return this;
}
throw new IllegalStateException("complete already: " + this);
}
private boolean setSuccess0(V result) {
if (isDone()) {
return false;
}
synchronized (this) {
// Allow only once.
if (isDone()) {
return false;
}
if (result == null) {
this.result = SUCCESS;//tag4.4.1
} else {
this.result = result;
}
if (hasWaiters()) {
notifyAll();
}
}
return true;
}
tag4.3 见五
五 bind之ACCEPT注册
Fig 5.1
上述逻辑一样,最终执行到TailHandler这里,TailHandler没做什么,该方法没被override。HeadHandler做了acceptor的注册监听。
@Override
public void DefaultChannelPipeline.read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
@Override
public void AbstractChannel.AbstractUnsafebeginRead() {
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
调用了AbstractNioChannel override的doBeginRead@Override
protected void doBeginRead() throws Exception {
if (inputShutdown) {
return;
}
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
至此,整个DefaultPromise.bind方法执行完毕,下面开始执行DefaultPromise.sync()
。而此时在
tag4.4.已经将值设为SUCCESS了,所以不需要等待,直接返回。然后系统接着执行了
b.bind(port).sync().channel().closeFuture().sync();
的后半截方法“channel().closeFuture().sync()”方法。而由于closeFuture这个属性的执行结果一直没有赋值,所以被wait了,从而一直处于wait状态(Fig5.1
的line:45 run方法就是)。至此,主线程处于wait状态,并通过子线程无限循环,来完成客户端请求。