09 - ServerBootstrap-绑定过程doBind0
Posted 1160636144
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了09 - ServerBootstrap-绑定过程doBind0相关的知识,希望对你有一定的参考价值。
- 前面说ServerBootstrap的bind()方法内部进行了注册register和绑定bind0,第4节已经分析了register,这里分析bind0过程
class AbstractBootstrap { private ChannelFuture doBind(final SocketAddress localAddress) { ChannelFuture regFuture = initAndRegister(); // 初始化Channel并注册 Channel channel = regFuture.channel(); if (regFuture.isDone()) { // 注册Channel完成,调用doBind0绑定 ChannelPromise promise = channel.newPromise(); doBind0(regFuture, channel, localAddress, promise); return promise; } else { // 注册Channel未完成,添加操作完成监听并调用doBind0绑定 final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel); regFuture.addListener(new ChannelFutureListener() public void operationComplete(ChannelFuture future) throws Exception { Throwable cause = future.cause(); if (cause != null) { promise.setFailure(cause); } else { promise.registered(); doBind0(regFuture, channel, localAddress, promise); } } }); return promise; } } }
- doBind0内部会获取到该channel绑定的eventLoop并提交任务进行异步Bind。根据内部判断逻辑,只有regFuture.isSuccess()才会进行绑定,即只有注册成功之后才会执行bind操作。
class AbstractBootstrap { private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { channel.eventLoop().execute(new Runnable() { public void run() { if (regFuture.isSuccess()) { channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); } }
- 当eventLoop轮询线程获取到bind任务时,对于Channel来说可视为发生一次出栈事件。Channel会从其pipeline的tail开始查找首个覆写了bind方法的outBoundHandler(即: 找到headContext)然后调用其bind()方法。。
class AbstractChannel { public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { return pipeline.bind(localAddress, promise); } } class DefaultChannelPipeline { public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { return tail.bind(localAddress, promise); } } class AbstractChannelHandlerContext { public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) { final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND); // 找到首个覆写了bind方法的handler EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeBind(localAddress, promise) { // 调用bind方法 ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise) } } else { safeExecute(executor, new Runnable() { public void run() { next.invokeBind(localAddress, promise); } }, promise, null, false); } return promise; } }
- 在headContext覆写的bind方法中,调用了doBind()方法,内部调用了原生JavaNio的bind方法进行绑定。在doBind()成功之后,会提交个异步任务(invokeLater),来触发channelActive。在active内部进行注册了channel的interestOps。
class AbstractChannelHandlerContext { public final void bind(final SocketAddress localAddress, final ChannelPromise promise) { boolean wasActive = isActive(); doBind(localAddress) { javaChannel().bind(localAddress, config.getBacklog()); } // 完成绑定之后还做了其它事情,这里会递归调用pipeline上所有handler的channelActive方法 if (!wasActive && isActive()) { invokeLater(new Runnable() { public void run() { pipeline.fireChannelActive() { ((ChannelInboundHandler) handler()).channelActive(this); } } }); } safeSetSuccess(promise); } }
总结:
serverBootstrap的bind(port)方法: sb.bind( port ) |__ doBind() |__ initAndRegister() |__ ... |__ bind0() |__ channel.eventLoop().execute( bind() ) |__ nioEventLoop.run() |__ bind() |__ doBind() |__ eventLoop().execute( fireChannelActive() ) |__ fireChannelActive()
以上是关于09 - ServerBootstrap-绑定过程doBind0的主要内容,如果未能解决你的问题,请参考以下文章