Netty Server 启动流程

  在分析netty 服务端启动流程之前,我们先看下 java nio socket服务端的代码如何编写,因为netty nio 框架底层最终还是要用java nio那一套方式启动服务。

        //创建channel
        ServerSocketChannel channel = ServerSocketChannel.open();
        //绑定端口
        channel.socket().bind(new InetSocketAddress("localhost", 8080));
        //设置成非阻塞
        channel.configureBlocking(false);

        Selector selector = Selector.open();
        //注册到selector 对accept connection 感兴趣
        channel.register(selector, SelectionKey.OP_ACCEPT);
        //监听selector事件,对不同类型的事件做相应的处理
        .....

  下面来看下netty 启动socket 服务端的代码(Netty 版本4.1.10-Final)

        //接收连接请求的事件循环
        EventLoopGroup bossGroup =new NioEventLoopGroup();
        //接收IO读写的事件循环
        EventLoopGroup workerGroup =new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup).
            //指定Channel(netty中的)
            channel(NioServerSocketChannel.class)
            //sockect channel的处理器链
            .childHandler(new ServerInitializerXXX());
            
            //绑定端口
            ChannelFuture sync = serverBootstrap.bind(9000).
            sync();
            sync.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

代码中bind方法要传端口号,说明这是启动服务端的入口,接下来分析一下bind方法。代码一路跟踪下去,走到doBind方法。(分析的代码中砍掉了一些异常处理流程,和不重要的逻辑)

private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } 
        //else 里面的代码砍掉了
    }

这里我们可以猜到netty做了2件事情
1.initAndRegister 方法:创建了Channel,并且注册到selector
2.doBind0 方法:传入了SocketAddress,说明进行了端口绑定操作

接下来我们看下netty是如何创建channel,注册到selector的。进入initAndRegister 方法

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            //创建Channel对象
            channel = channelFactory.newChannel();
            //初始化channel
            init(channel);
        } catch (Throwable t) {
           //相关代码砍掉了
        }
        //将Channel对象注册到selector
        ChannelFuture regFuture = config().group().register(channel);
        //判断注册到selector 是否有异常,如果有异常关闭channel
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

initAndRegister 做了三件事情
一.通过 channelFactory 创建了channel对象。此处的channelFactory是在创建了ServerBootstrap对象后调用channel方法时创建.


 public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        //ServerBootstrap中的channelFactory引用ReflectiveChannelFactory对象。
        //ReflectiveChannelFactory 对象中引用了channel方法中传入的Channel.class,ReflectiveChannelFactory 通过反射的形式创建Channel对象
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }
  // 小插曲
//这里创建channel对象使用SelectorProvider.openServerSocketChannel() 的方式创建,使用原生的ServerSocketChannel.open()创建channel,会加锁有性能损失.(https://github.com/netty/netty/issues/2308)

二.通过init方法初始化channel

void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        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(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                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));
                    }
                });
            }
        });
    }

1.设置attribute
2.设置ChannelOption
3.注册了一个ChannelHandler,在channel pipleline中出入了一个 ServerBootstrapAcceptor对象。ServerBootstrapAcceptor主要用来将新的请求转发到child eventLoopGroup.
这块逻辑不是很重要

三.channel 注册

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

一路跟踪到AbstractChannel 的register方法

  //砍掉了细枝末节的逻辑
 public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            AbstractChannel.this.eventLoop = eventLoop;
            //判断是不是当前线程在做register操作,保证一个线程负责一个channel的整个生命周期,避免线程安全问题
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                }
            }
        }

register0方法

private void register0(ChannelPromise promise) {
            try {
                boolean firstRegistration = neverRegistered;
                doRegister();
                neverRegistered = false;
                registered = true;
                
                pipeline.invokeHandlerAddedIfNeeded();
                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();
                
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        beginRead();
                    }
                }
            } catch (Throwable t) {
            }
        }

1.doRegister :将channel 注册到selector
2.invokeHandlerAddedIfNeeded :调用pipeline中channel的handlerAdded方法
3.fireChannelRegistered:调用pipeline中channelRegister方法
4.判断channel是否是active状态,如果channel 绑定了端口认为是active。此时还没有走到bind端口,不会触发调用ChannelActive方法。

Channel创建完成,注册到selector后开始绑定端口。我们回到doBind0方法,代码跟踪到AbstractChannelHandlerContext的bind 方法

public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        //在一个线程中执行channel的端口绑定
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }

再一路跟踪到AbstractChannel的bind 方法

          //判断是否绑定端口
          boolean wasActive = isActive();
            try {
               // 执行ServerSocketChannel的bind方法,绑定端口
                doBind(localAddress);
            } catch (Throwable t) {
            }
            if (!wasActive && isActive()) {
               //异步触发调用pipeline中channelHandler的ChannelActive方法
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }
总结

netty 服务端启动流程
1.创建channel对象,设置option,attribute,添加ServerBootstrapAcceptor 接收器。
2.channel 注册到selector
3.触发channelAdd,channelRegistered
4.bind 端口
5.触发ChannelActive

参考文章

netty源码分析之服务端启动全解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值