ServerBootstrap绑定和连接过程源码分析

本文解析了典型的Netty服务器端代码,介绍了如何通过两个EventLoopGroup线程池处理客户端连接和IO事件,以及如何配置Channel和ChannelHandler来实现业务功能。

转载自:http://blog.youkuaiyun.com/iter_zc/article/details/39349177
    https://segmentfault.com/a/1190000007283053
    https://segmentfault.com/a/1190000007403873

  典型的Netty服务器端代码如下,首先需要提供两个EventLoopGroup线程池,bossGroup是用来接受客户端TCP连接,workGroup是用来处理IO事件。然后指定采用何种Channel,Netty抽象了一组Channel的结构,和Java本身的Channel概念基本一致,NioServerSocketChannel相当于ServerSocketChannel。最后指定一组ChannelHandler来处理业务功能。(基于4.1.11.Final)

        EventLoopGroup bossGroup = new NioEventLoopGroup();  
        EventLoopGroup workerGroup = new NioEventLoopGroup();  
        try {  
            ServerBootstrap b = new ServerBootstrap();  
            b.group(bossGroup, workerGroup)  
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100) // 设置tcp协议的请求等待队列
                    .option(ChannelOption.SO_BACKLOG, 1024)  
                    .childHandler(new ChildChannelHandler());  

            ChannelFuture f = b.bind(port).sync();  
            System.out.println("Netty time Server started at port " + port);  
            f.channel().closeFuture().sync();  
        } finally {  
            bossGroup.shutdownGracefully();  
            workerGroup.shutdownGracefully();  
        }  

  直接看ServerBootstrap.bind(port),实际上是调用的AbstractBootstrap#bind()

    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }
    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        return doBind(localAddress);
    }
private ChannelFuture doBind(final SocketAddress localAddress) {
        // 初始化并注册一个Channel
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        // 注册成功
        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            // 调用doBind0绑定
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            //...略
        }
    }
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            // 新建一个Channel
            channel = channelFactory.newChannel();
            // 初始化Channel
            init(channel);
        } catch (Throwable t) {
            //...略
        }

        // 向EventLoopGroup中注册一个channel
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        return regFuture;
    }

  initAndRegister代码着重分析一下,这里的channelFactory就是根据channel(NioServerSocketChannel.class)传入的Class对象,实例化ReflectiveChannelFactory对象,channelFactory返回的是NioServerSocketChannel:

    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }

    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

  首先调用无参构造函数,newSocket()利用 SelectorProvider.openServerSocketChannel(),产生一个 ServerSocketChannel 对象。该构造函数会调用下面那个构造函数,可以看到有个SelectionKey.OP_ACCEPT参数。NioServerSocketChannel继承关系链是:NioServerSocketChannel<- AbstractNioMessageChannel<- AbstractNioChannel<- AbstractChannel,右边是它的父类。
  逐层跟踪源码:

    protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent, ch, readInterestOp);
    }
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            ch.configureBlocking(false);
        } catch (IOException e) {
            //…略
        }
    }
    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

  在AbstractNioChannel中,将readInterestOp设置为了SelectionKey.OP_ACCEPT,同时channel设置为非阻塞模式,在AbstractChannel中实例化一个unsafe和pipeline,具体的实例分别是NioMessageUnsafeDefaultChannelPipeline

  回到AbstractBootstrap#initAndRegister方法中,newChannel()完成之后,就对该channel做init工作,实质上调用的是ServerBootstrap中的init:

    @Override
    void init(Channel channel) throws Exception {
        // 设置配置的option参数
        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());
            }
        }

        // 获取绑定的pipeline
        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()));
        }

        // 为NioServerSocketChannel的pipeline添加一个初始化Handler,
        // 当NioServerSocketChannel在EventLoop注册成功时,该handler的init方法将被调用
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                //如果用户配置过Handler
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        // 为NioServerSocketChannel的pipeline添加ServerBootstrapAcceptor处理器
                        // 该Handler主要用来将新创建的NioSocketChannel注册到EventLoopGroup中
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

  先给NioServerSocketChannel设置Socket参数和附加属性,然后给NioServerSocketChannel关联的DefaultChannelPipeline中添加了一个ChannelInitializer,而这个 ChannelInitializer中添加了一个关键的ServerBootstrapAcceptor handler,这里会把currentChildGroup即workerGroup作为构造函数参数传入ServerBootstrapAcceptor,还有childHandler、childOptions、childAttrs,childHandler即服务器端代码中bootstrap.childHandler(…)所指的内容。

  再回到AbstractBootstrap#initAndRegister方法继续向下看,init之后,config().group().register(channel)会将NioServerSocketChannel注册到EventLoop中。config().group()获取的EventLoopGroup是AbstractBootstrap的成员,该值即ServerBootstrap.group(parentGroup, childGroup)中的parentGroup,即常说到的bossGroup。
  因此group().register()调用的实际上是MultithreadEventLoopGroup.register()方法:

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

  next()返回EventExecutor[] children中的一个EventExecutor来完成register。 EventExecutor实际上就是NioEventLoop,即从bossGroup中选一个NioEventLoop出来,最终调用NioEventLoop的父类SingleThreadEventLoop的register方法。

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

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

  在channel().unsafe().register()中会将EventLoop赋值给AbstractChannel内部的 eventLoop字段,到这里就完成了bossGroup 的EventLoop与NioServerSocketChannel的关联过程:

        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            //...略

            AbstractChannel.this.eventLoop = eventLoop;

            // 必须要保证注册是由该EventLoop发起的
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    // 如果不是单独封装成一个task异步执行
                    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);
                }
            }
        }

  在注册NioServerSocketChannel的过程中,会在AbstractChannel$AbstractUnsafe#register中调用eventLoop.execute方法。因为整个代码都是在主线程中运行的, 因此上面的eventLoop.inEventLoop()就为false, 于是进入到else分支, 在这个分支中调用了eventLoop.execute。再次强调一下,这里的eventLoop是bossGroup中的一个eventLoop。eventLoop是一个NioEventLoop的实例,最终调用的是 SingleThreadEventExecutor#execute:

    @Override
    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }

        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            addTask(task);
        } else {
            startThread();
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }

        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }

  已经分析过inEventLoop == false, 因此执行到else分支, 在这里就调用了startThread() 方法来启动 SingleThreadEventExecutor内部关联的Java线程。

    private void startThread() {
        if (state == ST_NOT_STARTED) {
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                doStartThread();
            }
        }
    }

  STATE_UPDATER是SingleThreadEventExecutor内部维护的一个属性, 它的作用是标识当前的thread的状态. 在初始的时候, STATE_UPDATER == ST_NOT_STARTED, 因此第一次调用 startThread()方法时, 就会进入到if语句内, 进而调用到 doStartThread()
  startThread()方法之后接着是addTask(task)方法,addTask是将task,即:

           new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
           }

  放入到taskQueue队列中去,之后会有线程从taskQueue取出任务执行。register0的主要作用是注册ChannelPipeLine。

  来看看AbstractChannel#register0(promise)做了什么:

        private void register0(ChannelPromise promise) {
            try {
                //...略
                boolean firstRegistration = neverRegistered;

                // 真正的注册动作
                doRegister();
                neverRegistered = false;
                registered = true;

                //...略
                pipeline.invokeHandlerAddedIfNeeded();

                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                //...略
            }
        }

  调用 doRegister()后,然后调用pipeline.invokeHandlerAddedIfNeeded(),至于其内部如何调用,之后剖析pipeline时再做详细分析。然后将promise设置为success状态,表示注册过程完成。pipeline.fireChannelRegistered()触发一个ChannelRegistered事件,会从DefaultChannelPipeline的head开始,最终传递tail即TailContext来处理该事件,TailContext的channelRegistered方法也是个空实现。

  回头来看AbstractBootstrap#doBind,先判断initAndRegister返回的regFuture是否完成,在AbstractChannel#register0(promise)中,有设置success状态,因此会继续执行下面的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());
                }
            }
        });
    }

  这里的channel即NioServerSocketChannel,其关联的eventLoop即bossGroup中的一个,eventLoop的execute方法,之前有说过eventLoop的execute方法,会将task放在taskQueue中等待线程来执行。
  那先来看channel.bind(),即DefaultChannelPipeline#bind:

    public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);
    }
    @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        //...略

        final AbstractChannelHandlerContext next = findContextOutbound();
        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);
        }
        return promise;
    }

    private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

  可以看出,所获取的节点是从tail开始遍历,获取第一个节点属性outbound为true的节点。其实最终会返回 head 节点。获取该节点后,调用 invokeBind(),如下:

   private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
        if (invokeHandler()) {
            try {
                ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
            } catch (Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        } else {
            bind(localAddress, promise);
        }
    }

  handler()返回的是 HeadContext 对象,然后调用其bind()

        public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
                throws Exception {
            unsafe.bind(localAddress, promise);
        }
        @Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            //...略

            boolean wasActive = isActive();
            try {
                // 最核心方法
                doBind(localAddress);
            } catch (Throwable t) {
                //...略
            }

            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

  doBind(localAddress)最终调用的是NioServerSocketChannel#bind来完成端口的绑定:

    @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

  然后会调用pipeline.fireChannelActive()触发ChannelActive事件:

    public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }

  会调用head即HeadContext的channelActive:

        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();

            readIfIsAutoRead();
        }
        private void readIfIsAutoRead() {
            if (channel.config().isAutoRead()) {
                channel.read();
            }
        }

  HeadContext会主动发起一个outbound的读事件,通过Pipeline最后传递到HeadContext的read方法。HeadHandler处理read事件时,实际上调用了unsafe的beginRead方法,最后到了AbstractNioChannel#doBeginRead()

    @Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;

        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

  根据之前的介绍已经知道,此时的readInterestOps是OP_ACCEPT = 16selectionKey.interestOps=0;因此进入到 selectionKey.interestOps(interestOps|readInterestOp);使用或操作设置selectionKey的interestOps为OP_ACCEPT,表示开始监听网络连接。

  前面提到了SingleThreadEventExecutor#startThread()方法中会调用doStartThread()来启动SingleThreadEventExecutor内部关联的Java线程:

    private void doStartThread() {
        assert thread == null;
        executor.execute(new Runnable() {
            @Override
            public void run() {
                thread = Thread.currentThread();
                if (interrupted) {
                    thread.interrupt();
                }

                boolean success = false;
                updateLastExecutionTime();
                try {
                    SingleThreadEventExecutor.this.run();
                    success = true;
                } catch (Throwable t) {
                    //...略
                } finally {
                    //...略
            }
        });
    }

  这里面最重要的是SingleThreadEventExecutor.this.run(),实际上是NioEventLoop#run:

    @Override
    protected void run() {
        for (;;) {
            try {
                //...略

                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        runAllTasks();
                    }
                } else {
                    final long ioStartTime = System.nanoTime();
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        final long ioTime = System.nanoTime() - ioStartTime;
                        runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
            //...略
        }
    }

  这里会调用processSelectedKeys()来处理感兴趣的OP_ACCEPT事件,调用链是NioEventLoop#processSelectedKey -> NioMessageUnsafe#read,在NioMessageUnsafe中会触发pipeline的事件;同时,还会有runAllTasks(),来处理taskQueue队列中的任务。可以设置ioRatio,来控制执行IO事件和非IO事件的时间比。

  看下NioMessageUnsafe#read

        @Override
        public void read() {
            //...略
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);

            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        int localRead = doReadMessages(readBuf);
                        //...略
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (exception != null) {
                    closed = closeOnReadError(exception);

                    pipeline.fireExceptionCaught(exception);
                }

                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } finally {
                // Check if there is a readPending which was not processed yet.
                // This could be for two reasons:
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
                //
                // See https://github.com/netty/netty/issues/2254
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }

  doReadMessages方法实际上接受客户端连接,并创建了NioSocketChannel:

    protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = SocketUtils.accept(javaChannel());

        try {
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            //...略
        }

        return 0;
    }
    public NioSocketChannel(Channel parent, SocketChannel socket) {
        super(parent, socket);
        config = new NioSocketChannelConfig(this, socket.socket());
    }
    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ);
    }

  同之前的NioServerSocketChannel的构建类似,构造NioSocketChannel时传入了SelectionKey.OP_READ

  之后对readBuf中的每个NioSocketChannel触发pipeline的ChannelRead事件:

    public final ChannelPipeline fireChannelRead(Object msg) {
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }

  ChannelRead会从head开始传递,调用pipeline链中的InboundContext的channelRead方法,之前ServerBootstrapAcceptor是加到了pipeline链中的,而且它继承了ChannelInboundHandlerAdapter,因此会调用ServerBootstrapAcceptor的channelRead方法:

        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            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() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

  这里的msg 即channel,是一个NioSocketChannel的实例, childGroup即workerGroup,于是workerGroup的EventLoop和NioSocketChannel 关联了。注意这里的childGroup.register(child),类似与前面的介绍,最终调用了AbstractNioChannel#doBeginRead方法,将SelectionKey.OP_READ添加到了selectionKey中。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值