Netty NioServerSocket 啓動流程1

轉載請附上原文鏈接

1.首先貼io.netty.example.echo.EchoServer的EchoServer的部分,這個是Netty官方給的例子。

#1

public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(serverHandler);
                 }
             });

            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

2. ServerBootstrap先把各種配置類讓你配好,配置過程也就是第一點的代碼塊中的22-26那一堆鏈式的調用,這裏大概說一下這個鏈式調用裏面幹了什麼,其實就是ServerBootStrap裏面有一大堆屬性,然後最裏面做的事情其實是給ServerBootStrap這些屬性賦值的操作,賦值的屬性給之後進行的初始化-註冊-綁定的操作做基礎的。然後調用ServerBootstap的bind開始綁定服務器,然後這個bind只是調用了它實現的AbsractBootstrap中的bind類,ServerBootrap繼承了AbstractBootstrap但是他又沒有bind方法,所以調用的是AbstractBootstrap裏面的bind方法。然後裏面真正幹事的是ChannelFuture doBind(final SocketAddress localAddress)方法,代碼如下面的#1代碼塊所示。這裏面首先調用了AbstractBootstrap的initAndRegister()方法。這裏有很多陌生的類我這裏大概說一下這些類是幹什麼的。(我當初學源碼的時候非常迷茫,我看的源碼的教程,有一些東西的作用是所有東西學完了,最後纔講,但是不符合我的思維習慣,所以我覺得自己寫一個真正適合自己的筆記)

首先,initAndRegister()他會返回一個實現了ChannelFuture的類,而這裏返回的類是DefaultChannelPromise,那這個類其實在學源碼的過程中會碰見很多次,很多源碼教程讓你看到這個類的時候忽略掉他,不過我這裏會首先講一下這個東西的大概作用,把他當作api來用,然後不至於在後面看到他的各種調用的時候而感到迷茫。他是Netty封裝的一個用於異步調用的類,Netty實現的NioServerSocketChannel是一個異步非阻塞的服務器,非阻塞是依賴於NIO的(不懂的可以去學一下NIO,這篇文章主要還是介紹netty)。

而Netty裏面的DefaultChannelPromise的異步怎麼理解呢?先講一下對同步和異步的理解,同步就是A交給B一個任務,然後B去執行,A等B這個任務執行完了之後,A繼續幹別的事情。而異步的意思是A交給B一個任務,B去執行,但是A自己直接去幹別的事情了,然後B幹完了自己手上的事情之後,B通知A任務做完了,然後A去執行相應的參數。而這個DefaultChannelPromise就是幹這個事情的,由於initAndRegister()方法在代碼裏面 本身是同步的,但是他裏面一些任務是通過提交給一個線程池來實現的(Netty自己實現的串行無鎖化的線程池,也就是說,任務都在一個隊列裏面,在某個地方,按照順序把任務一個一個取出來順序執行),而這個DefaultChannelPromise保持了對這個線程池的引用,而某些任務的某個地方通過某個api(後面會詳細講)執行相應的回調函數。而這個回調函數就是通過DefaultChannelPromise去添加的。

#1

private ChannelFuture doBind(final SocketAddress localAddress) {
    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(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);
        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;
    }
}

3 initAndRegister()方法裏面主要流程分爲3步

第一步也就是把AbstractBootstrap中的channel屬性賦值,用了channelFactory.newChannel()方法,之前鏈式調用做設置的時候有一個屬性是b.channel(NioServerSocketChannel.class),那這裏面做了兩件事,首先創建了一個ReflectiveChannelFactory類,調用的是一個以Class爲參數的構造方法,裏面進去的也就是NioServerSocketChannel.class(可以看到3.#2@1的方法),然後在創建完這個類之後,把創建完的Reflective類的對象作爲參數傳入channelFactory方法,然後看到3.#2@2 和 3.#2@3,也就印證了我之前說的,這組鏈式調用是用於給AbstractBootStrap裏面的屬性賦值的(服務器真正調用的是ServerBootStrap)。從名字來看,這是一個通過反射來創建類的方法的工廠,然後回到#1的第四行,有一個channelFactory.newChannel()方法,而這個channelFactory的值是什麼呢,由於b.bind發生在那個鏈式調用的後面,鏈式調用到的b.channel(NioServerSocketChannel.class);就給AbstractBootstrap(由於此時的實際調用者是ServerBootstrap,所以當且僅當ServerBootstrap沒有屬性或者方法的時候,纔會調用到AbstractBootstrap方法)中的channelFactory賦值了,那麼#1第四行,的channelFacotry也就是之前設置的ReflectiveChannelFactory工廠,那就會調用到這個工廠裏面的newChannel()方法,我把他貼在了3.#2@4,可以看到他以反射方式調用了clazz的構造函數來創建對象,這個clazz是什麼呢,其實是之前在創建ReflectiveChannelFactory時作爲構造函數傳進去的NioServerSocketChannel.class,那麼也就是說,這裏調用的是NioServerSocketChannel的無參構造方法,那麼這時候就該去到他的無參構造方法繼續分析了。

而NioServerSocketChannel的無參構造方法調用了另外一個構造方法NioServerSocketChannel(SelectorProvider provider) ,

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

public NioServerSocketChannel() {

this(newSocket(DEFAULT_SELECTOR_PROVIDER));

}

而在這個NioServerSocketChannel中的newSocket方法調用了provider.openServerSocketChannel();

,這個就是原生NIO創建ServerSocketChannel的方法。也就是說NioServerSocketChannel是Netty自己對於原生NIO的一個封裝。可以看到#3的NioServerSocketChannel也調用了他父類的構造方法然後他父類的構造方法繼續調用了他父類的構造方法,總結一下,這組構造方法幹了這麼幾件事,1.設置感興趣的事件(這個應該是給原生NIO用的),2.把創建的NioServerSocketChannel對象封裝在抽象類裏面(稍微補充一下爲什麼這麼做,因爲Netty本來就不只有NIO,抽象類是把不同的服務器抽取出來的公共代碼,定義了這些服務器的公共行爲,這樣做的好處就是,以後要擴展的時候,你只要按照他提供的接口的規範去擴展就好了,當然這個和Netty無關,這就回到Java本身應該怎麼去寫代碼的問題上了)3.創建了Unsafe類和ChannelPipeline類,這兩個類非常重要,我會在講init的地方中斷,然後開始講這兩個方法。NioServerSocketChannel中的config屬性就是在這裏被賦值的,config就是NioServerSocketChannelConfig類的變量,他包裝了NioServerSocketChannel本身以及原生的NIO socketChannel,其實這只是給NioServerSocketChannel裏面的一個屬性賦值而已,但是爲什麼要有config這個屬性而不是直接把這些東西放在,我的猜測是,這些事NioServerSocketChannel獨立於抽象類的屬性,然後別人用的時候可以拿到這裏面的東西。

第二部就是init方法了,在創建完那個NioServerSocketChannel之後,就調用ServerBootstrap中的init(channel)來對NioServerSocketChannel對象進行初始化,代碼放在4.#4@1,然後出現了幾個陌生的東西,他們分別爲pipeline的相關的方法(比如p.addLast 方法),eventLoop().execute()方法。要搞懂這個地方,就得想搞懂Netty裏面的一些東西,也就是pipeline相關和eventLoop相關,從這裏開始,如果不搞懂Netty相關的其他知識點的話,這啓動流程之後的源碼將寸步難行。

#1

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

    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

#2 

//來自AbstractBootstrap類 @1
public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

//來自AbstractBootstrap類 @2,
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
    return channelFactory((ChannelFactory<C>) channelFactory);
}

//來自AbstractBootstrap類 @3
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
    if (channelFactory == null) {
        throw new NullPointerException("channelFactory");
    }
    if (this.channelFactory != null) {
        throw new IllegalStateException("channelFactory set already");
    }
    this.channelFactory = channelFactory;
    return self();
}

//來自AbstractBootstrap類 @4
@Override
public T newChannel() {
    try {
        return clazz.getConstructor().newInstance();
    } catch (Throwable t) {
        throw new ChannelException("Unable to create Channel from class " + clazz, t);
    }
}

#3 

//來自NioServerSocketChannel @1
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

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

//來自@2的super的連續調用
//AbstractNioMessageChannel
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent, ch, readInterestOp);
}

//來自@2的super的連續調用
//AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    try {
        ch.configureBlocking(false);
    } catch (IOException e) {
        try {
            ch.close();
        } catch (IOException e2) {
            if (logger.isWarnEnabled()) {
                logger.warn(
                        "Failed to close a partially initialized socket.", e2);
            }
        }

        throw new ChannelException("Failed to enter non-blocking mode.", e);
    }
}

//來自@2的super的連續調用
//AbstractChannel
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

#4 

//來自ServerBootstrap @1
    @Override
    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(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }

        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));
                    }
                });
            }
        });
    }

寫到最後,雖然學了一遍Netty的主要流程 源碼,但是我遠遠低估了源碼解讀類文章的工作量,所以Netty源碼解讀我打算當成一個系列來,不過我最近忙着準備面試,這個系列更新的應該不會很快。

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