Netty源碼(七):啓動源碼

1. 主線

our thread就是我們自己寫的netty服務器的主線程的代碼
在這裏插入圖片描述

2. 啓動

在下面三處打上斷點
在這裏插入圖片描述
在這裏插入圖片描述

2.1 selector初始化是在NioEventLoopGroup初始化

運行程序:發現,查看NioEventLoopGroup通過創建一個子的NioEventLoopGroup,然後子的NioEventLoopGroupNioEventLoop,並完成selector的創建
在這裏插入圖片描述

2.2 進入bind方法

在這裏插入圖片描述
跟進源碼:

private ChannelFuture doBind(final SocketAddress localAddress) {
        // channel註冊、初始化、綁定到EventLoopGroup上
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        // regFuture是異步的,到此處不一定會完成
        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 {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            // 如果沒有完成註冊操作會將註冊任務封裝成一個task,加入到regFuture的Listener上
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

2.2.1 initAndRegister方法的具體實現

在這裏插入圖片描述

2.2.1.1initAndRegister中的init()方法

@Override
    void init(Channel channel) {
        // 初始化option
        setChannelOptions(channel, newOptionsArray(), logger);
        // 初始化屬性
        setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));

        // 構建一個pipeline
        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;// 全局變量childGroup改名爲currentChildGroup
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
        }
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);

        // ChannelInitializer完成將我們需要的handler加入到pipeline中
        // 當將所有的handler加入到pipeline完成以後,ChannelInitializer結束,從pipeline中移除
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                // new ServerBootstrapAcceptor()對象時將currentChildGroup作爲參數傳入
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

2.2.1.2 initAndRegister方法中的register()

因爲next方法我們之前已經講過了兩種選擇器
在這裏插入圖片描述
跟進register()的代碼

 @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            ObjectUtil.checkNotNull(eventLoop, "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;

            // 判斷當前線程是否是NioEvenetLoop中的線程
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    // 如果當前線程不在NioEvenetLoop中,將register0任務丟到eventLoop去執行
                    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);
                }
            }
        }

繼續跟進register0方法

	@Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                // 使用java的jdk的Nio變成,將channel註冊到selector上
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }

2.2.2 doBind0方法的具體實現

doBind0中也是講bind任務放到eventLoop中
在這裏插入圖片描述
跟進bind源碼,他會讓pipeline去實現bind方法,我們直接進入pipelinde的head
在這裏插入圖片描述
在這裏插入圖片描述
找到bind方法
在這裏插入圖片描述
出現下面的代碼:

@Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            assertEventLoop();

            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }

            // See: https://github.com/netty/netty/issues/576
            if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
                localAddress instanceof InetSocketAddress &&
                !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
                !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                // Warn a user about the fact that a non-root user can't receive a
                // broadcast packet on *nix if the socket is bound on non-wildcard address.
                logger.warn(
                        "A non-root user can't receive a broadcast packet if the socket " +
                        "is not bound to a wildcard address; binding to a non-wildcard " +
                        "address (" + localAddress + ") anyway as requested.");
            }

            // 查看當前channel是否已經活躍
            boolean wasActive = isActive();
            try {
                // 完成bind
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }

            // bind完成以後完成激活channel
            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                    	// 激活channel 
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

2.2.2.1 追蹤bind方法

最後使用jdk的bind方法完成bind
在這裏插入圖片描述

2.2.2.2 pipeline.fireChannelActive();如何激活channel

在這裏插入圖片描述
不斷跟進readIfIsAutoRead(),發現使用的selectKey的ops操作。
在這裏插入圖片描述
總結:服務啓動的核心步驟
在這裏插入圖片描述
一些知識點總結:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章