Netty启动过程分析

在介绍基于Netty服务器启动过程之前,先来说说其相应的核心模块:

  1. 引导类(ServerBootstrap,BootStrap)
  2. 线程池(NioEventGroupLoop异步IO)
  3. Channel(与JAVA NIO中Channnel类似)
  4. ChannelPipeline(与Channel对应的流水线)
  5. Handler(Channel中可以指定相应的进行编解码等相应业务处理)

如果对Channel、ChannelPipeline、Handler之间关系不熟悉,请先了解Netty线程模型。接下来在Netty启动分析中之前,先了解下Netty的引导类,其核心类图如下:

由类图可以看到ServerBootstrap(服务端),BootStrap(客户端)均继承至AbstractBootStrap,下面给出ServerBootStrap和AbstractBootStrap关键成员变量。

类名ServerBootStrapAbstractBootStrap
Channel连接相应相应配置childOptionsoptions
参数childAttrsAttrs
线程组childGroupgroup
业务处理childHandlerHandler

表中对比了类中的成员变量,先前在Netty线程模型中介绍过,Netty服务端有两个线程池(bossgroup和workgroup)来处理网络事件,这里AbstractBootStrap成员变量group对应bossgroup,负责处理客户端认证,ServerBootStrap中的成员变量childGroup即为线程模型中的workGroup线程池,负责处理认证成功的网络连接。其他参数也对应着bossgroup和workgroup中相应设置。

在对引导类有一个初步认知之后,接下来我们从结合一段启动代码分析Netty的启动过程:

EventLoopGroup bossGroup = new NioEventLoopGroup();//创建线程池
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
/*
**配置线程池相应参树
*/
b.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
            .localAddress(new InetSocketAddress(port))
              .handler(new LoggingHandler())
            .childHandler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
            }
        });
        }
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();

代码中,创建Serverbootstrap对象之后,分别执行方法:

  1. group(EventLoopGroup bossGroup, EventLoopGroup childGroup)//初始化线程池
  2. channel(Class<? extends C> channelClass)//初始化bossGroup中channel
  3. localAddress(int inetPort)//初始化网络通信ip及端口
  4. childHandler(ChannelHandler childHandler)//设置childGroup中的childHandler
  5. bind(),//其启动服务

此外ServerBootstrap提供一些列public方法,来设置其成员变量及父类(AbstractBootstrap成员变量),相应方法如下:

public ServerBootstrap group(EventLoopGroup group);
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup);
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value);
public <T> ServerBootstrap childAttr(AttributeKey<T> childKey, T value);
public ServerBootstrap childHandler(ChannelHandler childHandler);

示例demo部分给出group(EventLoopGroup parentGroup, EventLoopGroup childGroup)源码:

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);// parentGroup传给AbstractBootstrap成员
        if (childGroup == null) {
            throw new NullPointerException("childGroup");
        }
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = childGroup;//childGroup传给ServerBootstrap成员
        return this;
    }

以看到group方法调用父类AbstractBootstrap的group(EventLoopGroup)方法,并初始化其成员变量childGroup;(先在这里说一下,这里的parentGroup对应Netty线程模型中处理客户端连接、认证的线程池,childGroup处理连接、认证成功客户端网络事件的线程池)。

在准备好所有初始化工作之后,netty就开始进入父类AbstractBootstrap的bind(),其代码如下:

public ChannelFuture bind() {
        validate();//检验相应初始化参数
        SocketAddress localAddress = this.localAddress;
        if (localAddress == null) {
            throw new IllegalStateException("localAddress not set");
        }
        return doBind(localAddress);
    }

可以看到bind()主要是调用了doBind(localAddress);

private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();//业务一
        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 {
                        promise.registered();
                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

上面给出代码主要执行了initAndRegister();doBind0(regFuture, channel, localAddress, promise);

final ChannelFuture initAndRegister() {
        Channel channel = null;
       …
        channel = channelFactory.newChannel();//NioServerSocketChannel对象
        init(channel);//初始化channel
        …
        ChannelFuture regFuture = config().group().register(channel);//将channel注册到线程return regFuture;
    }

在initAndRegister中先创建通过反射创建了一个NioServerSocketChannel实例,接着调用init方法初始化该NioServerSocketChannel对象,init(Channel 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));
                    }
                });
            }
        });
    }

init()方法设置了NioServerSocketChannel实例的attr、option等,同时添加一个LoggingHandler与ServerBootstrapAcceptor的hanlder,关于ServerBootstrapAcceptor待会说明它的作用;

接着config().group().register(channel),通过elcipse跟踪方法调用链是:

(SingnalThreadEventLoop)register(channel)->
SingnalThreadEventLoop.register(final ChannelPromise promise)->
AbstractUnsafe.register(EventLoop eventLoop, final ChannelPromise promise)->
       AbstractUnsafe.register0(ChannelPromise promise)

AbstractUnsafe.register0源码如下:

private void register0(ChannelPromise promise) {
          …
                doRegister();//将NioServerSocketChannel注册到Selector上
           …
                pipeline.invokeHandlerAddedIfNeeded();//将NioServerSocketChannel的handler添加至pipeline
…
               pipeline.fireChannelRegistered();//在Channel的hanlder链中传播register
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();//在handler链中传播active事件
                    } else if (config().isAutoRead()) {
                        beginRead();//设置Selector中selectionkey(readInterestOp = 0)
…
        }
}
}

在register0内,首先执行doRegister()->AbstractNioChannel. doRegister(),将NioServerSocketChannel注册到Selector上。
接着调用

pipeline.invokeHandlerAddedIfNeeded();
(DefaultChannelPipeline) invokeHandlerAddedIfNeeded()->callHandlerAddedForAllHandlers()
private void callHandlerAddedForAllHandlers() {
        final PendingHandlerCallback pendingHandlerCallbackHead;
        synchronized (this) {
            assert !registered;
            registered = true;
            pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
            this.pendingHandlerCallbackHead = null;
        }
        PendingHandlerCallback task = pendingHandlerCallbackHead;
        while (task != null) {
            task.execute();
            task = task.next;
        }
    }

pendingHandlerCallbackHead为DefaultChannelPipeline成员变量,在初始化调用ChannelPipeline中的addXxx(handler)系列方法时,会将handler添加至pendingHandlerCallbackHead中;

其调用链

task.execute()
->(PendingHandlerAddedTask)callHandlerAdded0()
->ctx.handler().handlerAdded(ctx)

将方法调用就是将addXxxv(handler)中handler添加至pipeline中;
接着调用pipeline.fireChannelRegistered()中在pipeline的handler链中传播注册事件
最后调用beginRead()->AbstractUnsafe. beginRead()方法,该方法的关键执行doBeginRead(),代码如下:

protected void doBeginRead() throws Exception {
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }
        readPending = true;
        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

doBeginRead()将Selector中selectionkey设置为读就绪(readInterestOp = 0
至此initAndRegister()方法执行完毕,开始执行doBind0()

private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {
        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()
->head.bind()
->AbstractUnsafe.bind()
->NioServerSocketChannel.bind()

服务端绑定端口,此时服务器可处理IO事件。
当客户端连接或由读事件时,下面给出线程池中线程执行体NioEvemtLoop相应IO事件processSelectedKey()源代码。

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
      …
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
                unsafe.finishConnect();
            }
…
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                ch.unsafe().forceFlush();
            }
…
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
…
 } 

当有客户端连接或读时间时,将会调用AbstractNioChannel.NioUnsafe的read()方法

public void read() {
 …
         do {
               int localRead = doReadMessages(readBuf);//添加NioSocketChannel至readBuf中
               if (localRead == 0) {
                    break;
               }
               if (localRead < 0) {
                    closed = true;
                    break;
               }
               allocHandle.incMessagesRead(localRead);
               } while (allocHandle.continueReading());
…

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    pipeline.fireChannelRead(readBuf.get(i));//在pipeline传播读事件
                }
…

                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();
…

    }

在read()方法的执行中主要调用了doReadMessages(readBuf)pipeline.fireChannelRead(readBuf.get(i));
doReadMessages(readBuf)代码如下:

protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = SocketUtils.accept(javaChannel());
…
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
           }
…
        return 0;
    }

该方法将连接的成功客户端SocketChannel添加的buf中。
pipeline.fireChannelRead(readBuf.get(i))调用(DefaultChannelPipeline)fireChannelRead(Object msg)循环处理注册的SocketChannel;相应调用链如下

(DefaultChannelPipeline)fireChannelRead(Object msg) 
->AbstractChannelHandlerContext.invokeChannelRead(head, msg)
-> AbstractChannelHandlerContext.invokeChannelRead(Object msg)
 -> ((ChannelInboundHandler) handler()).channelRead(this, msg);

由调用链可知,最后执行各入站处理器(ChannelInboundHandler)的channelRead()方法 。

现在我们再回过头来看服务端在初始化时,向NioServerSocektChannel中添加的ServerBootstrapAcceptor(handler)

private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
        private final EventLoopGroup childGroup;
        private final ChannelHandler childHandler;
        private final Entry<ChannelOption<?>, Object>[] childOptions;
        private final Entry<AttributeKey<?>, Object>[] childAttrs;
        private final Runnable enableAutoReadTask;
        ServerBootstrapAcceptor(
                final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
                Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
            this.childGroup = childGroup;
            this.childHandler = childHandler;
            this.childOptions = childOptions;
            this.childAttrs = childAttrs;
            enableAutoReadTask = new Runnable() {
                @Override
                public void run() {
                    channel.config().setAutoRead(true);//在客户端认证成功之后
                }
            };
        }
        @Override
        @SuppressWarnings("unchecked")
        public void channelRead(ChannelHandlerContext ctx, Object msg) {//将channel转移至childFGroup
            final Channel child = (Channel) msg;
            child.pipeline().addLast(childHandler);
            setChannelOptions(child, childOptions, logger);
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
            try {
                childGroup.register(child).addListener(new ChannelFutureListener() {//将NioSocketChannel注册到childGroup中
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }
   …
}

在客户端连接到达时,ServerBootstrapAcceptorchannelRead(ChannelHandlerContext ctx, Object msg)方法被调用,而在调用在方法时,传入Object msg是连接成功的NioSocketChannel,前文说过childGroup为处理连接成功客户端Channel事件线程池,childGroup.register(child)即将连接成功NioSocketChannel注册到childGroup中。到这里基于Nio 的Netty服务端启动流程分析完毕。

附本文贴出的源代码,来说Maven中央仓库,坐标如下:

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.15.Final</version>
</dependency>

此外,贴出的代码为了方便读者专注与代码主线流程,将一些异常处理,条件判断等等删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值