09 - ServerBootstrap-绑定过程doBind0

Posted 1160636144

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了09 - ServerBootstrap-绑定过程doBind0相关的知识,希望对你有一定的参考价值。

  1. 前面说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;
        }
    }
    }
  2. 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());
                }
            }
        });
    }
    }
  3. 当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;
    }
    }
  4. 在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的主要内容,如果未能解决你的问题,请参考以下文章

ServerBootstrap简介

[Netty源码] 服务端启动过程

浅谈Netty中ServerBootstrap服务端源码(含bind全流程)

Java开发之Netty

5.接入客户端连接

Netty 核心源码解读 —— ServerBootstrap 篇