<2021SC@SDUSC>ServerBootstrap绑定端口(二)

2021SC@SDUSC

一、前言

在本篇博客中,会承接上一篇博客中对服务器端的端口绑定方法的分析,继续学习剩余的内容,包括initAndRegister和dobind0等方法。

二、initAndRegister

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                
                channel.unsafe().closeForcibly();
               
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        return regFuture;
    }

首先,在try语句块中完成了对channel对象的创建,这里channelFactory的具体类型是ReflectiveChannelFactory类,newChannel的具体实现如代码所示。

    @Override
    public T newChannel() {
        try {
            return constructor.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }

可以看到,这里实际上是通过了构造器调用无参构造函数来创建的,而构造器则是在serverbootstrap的channel方法中指定的。

	//AbstractBootstrap类的channel方法,用于指定Channel类型
	//实际上是将channel的字节码对象作为参数,创造一个ReflectiveChannelFactory对象
    public B channel(Class<? extends C> channelClass) {
        return channelFactory(new ReflectiveChannelFactory<C>(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }
	//ReflectiveChannelFactory的构造函数
    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            this.constructor = clazz.getConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                    " does not have a public non-arg constructor", e);
        }
    }

至此,channel对象就被创建,之后回到initAndRegister方法,进入下一步,调用init方法。

    @Override
    void init(Channel channel) {
        setChannelOptions(channel, newOptionsArray(), logger);
        setAttributes(channel, newAttributesArray());

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

ServerBootstrap重写了AbstractBootstrap的init方法,所以,这里实际上调用的是ServerBootstrap类的init方法。
在init方法中,首先为一些选项赋值,比如在setChannelOptions中,

    static void setChannelOptions(
            Channel channel, Map.Entry<ChannelOption<?>, Object>[] options, InternalLogger logger) {
        for (Map.Entry<ChannelOption<?>, Object> e: options) {
            setChannelOption(channel, e.getKey(), e.getValue(), logger);
        }
    }

首先将option转化成list,再通过for循环,将每一个选项设置,比如,在server代码中,有一行

                    .option(ChannelOption.SO_BACKLOG, 128)

就是在这里将SO_BACKLOG=128设置的。
之后,通过channel的pipeline ()方法获得pipeline对象,再向管道中添加处理器,关于这一部分,将在后面分析。
到了这里,完成了对channel对象的初始化,之后开始register。

        ChannelFuture regFuture = config().group().register(channel);

这里,config().group()返回的是bossGroup。

    @Override
    public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }
        @Override
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }

这里,next()方法是通过chooser来选择下一个线程,实际上,如代码所示,就是自增取余,之后进入register(Channel channel)方法。

    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

这里的register实际上调用的是AbstractChannel类的内部类AbstractUnsafe的register方法。

        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            ObjectUtil.checkNotNull(eventLoop, "eventLoop");
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }

            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }

在这个方法里面,前面都是一些判断,来到eventLoop.inEventLoop(),这是判断要绑定的线程是否是当前线程,由于现在在初始化服务器,显然不是,进入else语句,让eventLoop自己去执行register0方法。
而通过register0方法,将当前线程注册。到这里,返回了ChannelFuture对象,之后的判断是为了面对异步编程可能出现的问题,这些在上一篇博客中由分析,因此,对initAndRegister的分析先到此为止,至于之前的pipeline的addLast的部分将在本篇博客的第四部分分析。

三、doBind0

    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());
                }
            }
        });
    }

在doBind0中,依然是创建新的线程,由该线程去完成实际逻辑,channel.bind()方法位于AbstractChannel中,直接调用了pipeline.pipeline()方法。

    @Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return pipeline.bind(localAddress, promise);
    }

而DefaultChannelPipeline调用了tail.bind()方法,tail实际上是AbstractChannelHandlerContex,可以理解为ChannelHandler的容器,在netty中,ChannelHandler以一个链的形式连接,而head和tail分别是该链的首和尾。

    @Override
    public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);
    }
    @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        ObjectUtil.checkNotNull(localAddress, "localAddress");
        if (isNotValidPromise(promise, false)) {
            // cancelled
            return promise;
        }

        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, false);
        }
        return promise;
    }

从代码可知,这里就是从tail开始反向遍历handler链,直到head,最后会调用AbstractChannel的内部类AbstractUnsafe类的bind方法,而在这个bind方法中,调用了doBind方法,实际上,绑定端口的真正工作其实是在这里完成。

四、pipeline的addLast方法

在DefaultChannelPipeline中,addLast方法实现如代码所示。

    @Override
    public final ChannelPipeline addLast(ChannelHandler... handlers) {
        return addLast(null, handlers);
    }
    
    @Override
    public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
        ObjectUtil.checkNotNull(handlers, "handlers");

        for (ChannelHandler h: handlers) {
            if (h == null) {
                break;
            }
            addLast(executor, null, h);
        }

        return this;
    }
    
    @Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);

            newCtx = newContext(group, filterName(name, handler), handler);

            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventLoop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                callHandlerAddedInEventLoop(newCtx, executor);
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }
    
    private static void checkMultiplicity(ChannelHandler handler) {
        if (handler instanceof ChannelHandlerAdapter) {
            ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
            if (!h.isSharable() && h.added) {
                throw new ChannelPipelineException(
                        h.getClass().getName() +
                        " is not a @Sharable handler, so can't be added or removed multiple times.");
            }
            h.added = true;
        }
    }

    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

可以看见,addLast方法最后调用的是public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler)方法。首先说明,一般添加的Handler是ChannelInitializer的对象,而根据继承树想上寻找,可以看到,ChannelInitializer确实继承自ChannelHandlerAdapter类,因此,在checkMultiplicity中,handler instanceof ChannelHandlerAdapter 的结果为true。会判断如果有Sharable注解,必须是单例对象,不可重复添加。
之后,创建DefaultChannelHandlerContext对象,与head和tail一致,然后,执行addLast0方法,在addLast0中,可以看到将新建的context的前驱设置为tail的prev,后继设置为tail,再将prev的后继设置为context,tail的前驱设置为context,明白,这就是一个双向链表。
关于pipeline添加handler,就分析这么多了,额外提一句,ChannelInitializer是一个特殊的ChannelHandler,在初始化完成后,会将自己从pipeline中移除。

五、总结

在本篇博客中,分析了ServerBootstrap绑定端口的剩余细节,包括initAndRegister方法,doBind0方法等。此外,还分析了pipeline添加handler的代码,这里提一下,一个channel对应一个pipeline,一个pipeline对应多个ChannelHandlerContext,而每一个ChannelHandlerContext对应一个ChannelHandler,而head和tail则是这个链的首和尾。另外,由于时间原因,就不会分析Bootstrap的connect方法,其逻辑大体上与ServerBootstrap的bind相似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东羚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值