Netty 4.0源碼分析1:服務端啓動過程當中的Channel與EventLoopGroup的註冊

啓動服務端的代碼以下:java

public static void main(String[] args) throws Exception {
        SelfSignedCertificate ssc = new SelfSignedCertificate();
        SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new SecureChatServerInitializer(sslCtx));

            b.bind(PORT).sync().channel().closeFuture().sync();//調用bind方法時啓動線程
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

其中ServerBootstrap的bind方法經過調用父類的initAndRegister構造一個Channel,並把Channel註冊到EventLoopGrouppromise

    final ChannelFuture initAndRegister() {
        final Channel channel = channelFactory().newChannel();
        try {
            init(channel);
        } catch (Throwable t) {
            channel.unsafe().closeForcibly();
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        ChannelFuture regFuture = group().register(channel);//把channel註冊到EventLoopGroup(NioEventLoopGroup)
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

註冊代碼以下ide

    public ChannelFuture register(Channel channel) {
        return next().register(channel);//從EventLoopGroup裏獲取一個NioEventLoop,並註冊channel
    }  
  public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        if (promise == null) {
            throw new NullPointerException("promise");
        }

        channel.unsafe().register(this, promise);//註冊過程實際上是把NioEventLoop(中的selector)註冊到channel
        return promise;
    }

能夠看到Channel註冊到EventLoopGroup的過程被轉換成NioEventLoop(中的selector)註冊到channel。oop

轉換後的註冊過程以下:this

        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }
            AbstractChannel.this.eventLoop = eventLoop;
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {//啓動的時候執行else中的代碼
                try {
                    eventLoop.execute(new OneTimeTask() {//這個方法調用回啓動
                        @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);
                }
            }
        }

啓動的時候執行else裏的代碼,調用eventLoop的execute方法,該方法啓動eventLoop中的線程,並把register0做爲一個任務,提交給eventLoop中的任務隊列。線程

register0的代碼以下:code

        private void register0(ChannelPromise promise) {
            try {
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = neverRegistered;
                doRegister();
                neverRegistered = false;
                registered = true;
                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();
                if (firstRegistration && isActive()) {
                    pipeline.fireChannelActive();
                }
            } catch (Throwable t) {
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                selectionKey = javaChannel().register(eventLoop().selector, 0, this);//SelectableChannel
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    throw e;
                }
            }
        }
    }

能夠看到最終的註冊過程是調用jdk的SelectableChannel的register方法,把NioEventLoop的selector註冊到了channel上。對象

下面再看看eventLoop中的線程執行循環,代碼以下:隊列

@Override
    protected void run() {
        for (;;) {
            boolean oldWakenUp = wakenUp.getAndSet(false);
            try {
                if (hasTasks()) {
                    selectNow();
                } else {
                    select(oldWakenUp);
                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                }
                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    processSelectedKeys();
                    runAllTasks(); //執行任務隊列中的任務
                } else {
                    final long ioStartTime = System.nanoTime();
                    processSelectedKeys();
                    final long ioTime = System.nanoTime() - ioStartTime;
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);//執行任務隊列中的任務
                }
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        break;
                    }
                }
            } catch (Throwable t) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    }

總結一下:ip

調用ServerBootstrap的bind方法啓動服務端的時候,先建立一個Channel對象,而後從NioEventLoopGroup裏獲取一個NioEventLoop,而後啓動NioEventLoop中的線程,同時把註冊任務提交到NioEventLoop中的任務隊列。NioEventLoop中的線程循環執行,在必定條件下會執行任務隊列中的註冊任務,從而把NioEventLoop的selector註冊到Channel對象。

相關文章
相關標籤/搜索