Netty(4)初步瞭解 Netty服務端初始化過程

weixin_33766168發表於2018-12-12

我們看Netty服務端初始化大致做了什麼

ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new MyServerInitializer());
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();

ServerBootstrap

ServerBootstrap負責初始化netty伺服器,並且開始監聽埠的socket請求。

7856459-89fa95df57379317.png
image.png

1. group

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

parentGroup就是bossGroup,childGroup就是workerGroup

super.group(parentGroup);呼叫的是父類AbstractBootstrap#group() 方法

public B group(EventLoopGroup group) {
        if (group == null) {
            throw new NullPointerException("group");
        }
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return (B) this;
    }

做了些基本的判斷,並且返回 (B)this。實際上,AbstractBootstrap 整個方法的呼叫,基本都是“鏈式呼叫”。

2. channel

public B channel(Class<? extends C> channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
    }
  • io.netty.channel.ReflectiveChannelFactory會將傳入的channelClass(在這這裡也就是NioServerSocketChannel.class)進行封裝
public ReflectiveChannelFactory(Class<? extends T> clazz) {
        if (clazz == null) {
            throw new NullPointerException("clazz");
        }
        this.clazz = clazz;
    }
  • 呼叫 channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory)方法,設定 channelFactory屬性。
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
        return channelFactory((ChannelFactory<C>) channelFactory);
    }

io.netty.channel.ChannelFactory ,Channel 工廠介面,用於建立 Channel 物件。NioServerSocketChannel.class就意味著之後建立的channel都是非阻塞。

3. childHandler
childHandler這裡主要就是定義後續每條連線的資料讀寫,業務處理邏輯,後面解析。

4. bind
bind()是非常核心的方法,之前的ServerBootstrap所做的事情都是為bind()做準備。bind() 方法,繫結埠,啟動服務端。

public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }
    
public ChannelFuture bind(SocketAddress localAddress) {
        // 校驗服務啟動需要的必要引數
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        // 繫結本地地址( 包括埠 )
        return doBind(localAddress);
    }
    
private ChannelFuture doBind(final SocketAddress localAddress) {
        // 初始化並註冊一個 Channel 物件,因為註冊是非同步的過程,所以返回一個 ChannelFuture 物件。
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        // 繫結 Channel 的埠,並註冊 Channel 到 SelectionKey 中。
        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;
        }
    }
  • final ChannelFuture regFuture = initAndRegister();
    初始化並註冊一個 Channel 物件。因為註冊是非同步的過程,所以返回一個 ChannelFuture 物件

  • 因為註冊是非同步的,有可能完成了,也有可能沒完成,所有程式碼中if-else分別處理已完成核未完成的情況。

  • doBind0(regFuture, channel, localAddress, promise);,繫結 Channel 的埠,並註冊 Channel 到 SelectionKey 中。

  • 如果非同步註冊對應的 ChanelFuture 未完成,則呼叫 ChannelFuture#addListener(ChannelFutureListener) 方法,新增監聽器,在註冊完成後,進行回撥執行 doBind0(...) 方法的邏輯。

Netty是一個事件驅動的框架,我們可以看到在doBind()方法中的註冊和繫結,在Netty中是非同步事件,其中註冊就是將Channel註冊到reactor(之後講),繫結是指Channel獲得一個TCP埠。
在Netty中,但凡跟Future字眼扯上關係的,基本都是非同步,比如這裡的ChannelFuture。

相關文章