Netty Server端启动分析(二)doBind分析

上一篇文章中,研究了 NioEventLoopGroup 到底是什么,本文主要从bind方法入手。

属性设置

属性设置即设置为channel设置一些参数:

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(group)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new HelloServerHandler());
                        }
                    });

总结可以分为以下几种:

  1. 设置group,即设置boss和woker的线程组,上面例子中,boss和woker公用线程组。
  2. 指定channel,即指定具体的channelFactory,即由于是服务端,所以此时channelFactoryNioServerSocketChannel,实际是通过反射泛型来创建对应channel。
  3. 设置option,即设置server的一些tcp的参数。
  4. 设置childOption,即设置server的client的一些参数。
  5. 给新channel设置childerhandler,即每连接一个channel,netty会制定一个对应channel与之对应,此时channel中初始化的handler就是源自于此。
  6. localAddress 方法 绑定端口
  7. 通过 handler 方法给server的handler增加处理逻辑。

bind操作

首先进入的是AbstractBootstrapbind 方法,核心逻辑在doBind方法中:

   private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();  // 初始化并且注册到selector中
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {  // 报错直接退出
            return regFuture;
        }

        if (regFuture.isDone()) {
            // 说明注册完成了
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // 基本快完成了,但是就是没完成,那么就通过监听器来帮助他完成
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // 失败了
                        promise.setFailure(cause);
                    } else {
                        // 使用监听来注册
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();
                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

上面代码只是定义了大体的框架,但不难理解:

  • 初始化channel
  • 注册channel
  • 绑定channel
initAndRegister

简化后的代码如下:

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly();
                // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
                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();
            }
        }
  1. 通过channelFactory反射获取一个channel,如果失败了,可能会抛出SocketException("too many open files") 异常。
  2. config().group().register(channel)给channel选择一个reactor线程,并注册进selector。
  3. 根据不同状态,发起不同通知。
init

上一步中,获取了channel,这一步则继续为channel设置一些属性,对于 ServerSocketChannel 来说,他也是一个netty中的channel,所以也需要设置对应handler。

    void init(Channel channel) throws Exception {
        ... 省略设置option
        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }
		// 添加一个channelInitializer
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();   // 此时传参的channel是一个NioServerSocketChannel
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                    // 给server的channel增加一个handler接收处理器
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

上面主要是构造了NioServerSocketChannel 数据,设置一些channel参数,并增加一个 ChannelInitializer 并将一个 ServerBootstrapAcceptor 作为server端接收器传入。

group和register

group 即获取当前定义的NioEventLoopGroup,看看register方法:

    public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }

前一个 next() 主要为通过负载策略,来将channel绑定到一个EventLoop上面,即这个channel中事件,都由该EventLoop处理。具体的策略为上一篇看到的 EventExecutorChooserFactory.EventExecutorChooser

再往下看 register

    public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }

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

此时channel为:NioServerSocketChannel
unsafe为:AbstractNioMessageChannel.NioMessageUnsafe

而register方法则是进入到 AbstractChannel.AbstractUnsafe 中,里面register方法,除了前面校验注册保证幂等性外,就是通过register0来进行实际的调用:

        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
			... 省略校验部分
            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() 判断是否在reactor线程中,从而决定register0的执行位置。关于这个后面文章单独分析。

总结

从整体代码复杂度来说,netty还是比较容易理解的。本文主要是以一些框架性源码进行分析,并未深入到细节,下一篇则主要对 register0doBind0 两个方法进行研究。

关注博主公众号: 六点A君。
哈哈哈,一起研究Netty:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值