在讲解Netty线程模型的时候总是在强调Netty的线程模型是基于主从Reactor模型,在整个Netty的初始化过程是怎么体现的呢?Netty服务端的启动流程是怎样的呢?为了更加顺畅的理解Netty的整个执行轨迹,特梳理了一下Netty服务端启动流程,见下图:

linux netty启动失败 address already in use_源码分析Netty服务端启动流程

Netty服务端初始化序列图

提供如下Netty服务端示例代码:

linux netty启动失败 address already in use_Netty源码专题研究_02

linux netty启动失败 address already in use_线程组_03

 


1、创建ServerBootstrap实例。


2、调用ServerBootstrap的group方法,设置主,从线程组。对应Reactor模式的主Reactor、从Reactor。


3、调用channel方法,传入NioServerSocketChannel,表示创建的通道为服务端通道。


4、调用childHandler方法,传入的是用户自定义的初始化Handler链,该定制类继承自ChannelInitializer类,完成Handler的编排。


5、调用bind()方法,具体实现在AbstractBootstrap中。


5.1 调用initAndRegister


5.1.1 调用newChannel,创建NioServerSocketChannel。


5.1.2 调用init()方法,具体实现在ServerBootstrap,主要完成如下三件事:


       1)用相关选项配置Channel(NioSocketChannel)


       2)将childHandler(ChannelInitializer子类handler)加入到管道,已便后续注册应用程序编排的处理链条(Handler)。


       3)增加ServerBootstrapAcceptor,Handler处理器,主要是channelRead事件的处理,后续会重点讲解。


5.2 调用gourp().register()方法


     gourp()返回的是主Reactor线程组,也就是示例程序boosGroup线程组,register的具体实现类为SingleThreadEventLoop。最终会调用netty Channel,NioServerSocketChannel.register方法,然后调用相应的Unsafe的register方法,最终会调用java.nio.ServerSocketChannel完成注册操作,并用channelRegister,通过ChannelPipeline.fireChannelRegister传播事件。


    channelRegister事件会被一个重要的handler所处理,也就是我们自定义的Handler,用于服务编排,也就是步骤4中提到的ChannelInitializer子类。


    首先,initChannel 方法被调用,将用户编排的handler一个一个加入到管道。


    然后第二步,把自己从管道中移除,亮点,也就是自定义的Handler只会运行一次。亮点所在呀。


   第三步,继续传播注册事件。


   经过channelRegiser事件的处理后,在服务端,ServerBootstratAdapter会作为处理链条中的第二个,而该类的channelRead事件是核心所在,下文会将在何时触发。


经过上述步骤后,netty服务器就在监听端口等待客户端的接入,事件触发器select()会运行。


一旦有客户端请求连接,服务端会接受到accept事件,然后对该键进行处理:


linux netty启动失败 address already in use_源码分析_04

 

 

 

 


该段代码应该不会陌生,见于NioEventLoop的processSelectedKey()方法,得知,accept事件,会触发read操作,NioServerSocketChannel在处理读事件时,重写了doReadMessages,此方法,创建一个NioSocketChannel,作为message,传播到Handler进行处理。创建的message即NioServerSocketChannel,会被ServerBootstratAdapter的channelRead方法处理,该方法的核心就是用从Reactor线程组将channel注册到selector上【  childGroup.register(child).addListener】,Reactor主从模式体现的淋漓尽致。


下面列出ServerBootstratAdapter的channelRead方法:


@Override
        @SuppressWarnings("unchecked")
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            final Channel child = (Channel) msg;
            child.pipeline().addLast(childHandler);
            for (Entry<ChannelOption<?>, Object> e: childOptions) {
                try {
                    if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
                        logger.warn("Unknown channel option: " + e);
                    }
                } catch (Throwable t) {
                    logger.warn("Failed to set a channel option: " + child, t);
                }
            }
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
            try {
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }

本文以Netty服务端启动实例代码为视角,将Netty服务端的启动流程用序列图进行展开描述,清晰的剖析了Netty服务器端口绑定、主从Reactor线程模型、业务Handler编排细节。