Netty源碼-Channel講解

Channel的講解

  • 閱讀前需思考的問題
  • Netty中的Channel是什么?
  • Channel是怎么創(chuàng)建的?
  • Channel的創(chuàng)建時機?
  • Netty中的Channel和JDK中的Channel什么關系?
  • 兩者什么時候關聯(lián)起來的,怎么關聯(lián)的?

Channel是什么?

Channel是對socket連接的封裝,可以理解成Channel就是socket連接;Netty中的Channel分為NioSocketChannel(客戶端)和NioServerSocketChannel(服務端)兩種。 我們知道Netty是基于Java Nio的,那么與Java中的SocketChannel什么關系呢?是一對一的關系。

NioSocketChannel          --------->     SocketChannel           (1:1)
NioServerSocketChannel    --------->     ServerSocketChannel     (1:1)

Channel如何實例化的?

image.png

上圖是Netty官網的Echo Server示例,左邊是client端代碼,右邊是server端代碼。NioSocketChannel和和NioServerSocketChannel都賦值給channel(...)方法了。

點進ServerBootstrap的channel方法一看究竟:

## AbstractBootstrap類

public B channel(Class<? extends C> channelClass) {
    // 以channelClass為參數(shù),創(chuàng)建了ReflectiveChannelFactory對象
    // 從名字上看,是通過反射技術創(chuàng)建channel實例的工廠
    return channelFactory(new ReflectiveChannelFactory<C>(
            ObjectUtil.checkNotNull(channelClass, "channelClass")
    ));
}

繼續(xù)點進去:

## ReflectiveChannelFactory類

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    // 構造函數(shù)成員變量:constructor
    private final Constructor<? extends T> constructor;

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            // 獲取一個無參構造函數(shù)
            this.constructor = clazz.getConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                    " does not have a public non-arg constructor", e);
        }
    }

    @Override
    public T newChannel() {
        try {
            // 通過無參構造函數(shù),創(chuàng)建一個實例
            return constructor.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }
    ......
}

到此我們知道,在 newChannel() 方法中利用 反射技術 創(chuàng)建Channel實例的,此類使用了我們非常熟悉的工廠設計模式。

思考:我們知道ReflectiveChannelFactory是利用反射技術創(chuàng)建對象的,是不是可以創(chuàng)建任意對象?不是的,泛型T是有約束條件的,T 必須是實現(xiàn)Channel接口的類才行的。

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> 
## 泛型T是有約束條件的,T 必須是實現(xiàn)Channel接口的類

Channel無參構造函數(shù)做了什么?

來看下NioServerSocketChannel類:

## NioServerSocketChannel類

public NioServerSocketChannel() {
    // 創(chuàng)建一個Socket連接,傳遞給了一個參數(shù)的構造函數(shù)
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

// 創(chuàng)建了一個服務端Socket連接
private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        // 使用JDK的SelectorProvider,開啟了一個SocketChannel
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException(
                "Failed to open a server socket.", e);
    }
}

public NioServerSocketChannel(ServerSocketChannel channel) {
        // 創(chuàng)建好的SocketChannel傳遞父類構造函數(shù),同時Accpet事件也傳遞給了父類,后面會用到
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

到此可知,Netty的Channel中維護著一個JDK的SocketChannel,所以Netty的Channel和JDK的Channel關系是1:1。

我們知道Nio是非阻塞的,來看看什么時候配置?
一直點super(...)下去,直到:

## AbstractNioChannel類

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    // 客戶端監(jiān)聽OP_READ事件,服務端監(jiān)聽OP_ACCEPT事件,后面會用到
    this.readInterestOp = readInterestOp;
    try {
        // 客戶端和服務端的SocketChannel都需要配置非阻塞
        ch.configureBlocking(false);
    } catch (IOException e) {
        try {
            ch.close();
        } catch (IOException e2) {
            logger.warn(
                        "Failed to close a partially initialized socket.", e2);
        }

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

Channel何時被創(chuàng)建的?

  • 對于NioSocketChannel來說,它是在Bootstrap的connect(...)時被創(chuàng)建的。
  • 對于NioServerSocketChannel來說,它是在ServerBootstrap的bind(...)時被創(chuàng)建的。

以服務端的NioServerSocketChannel來研究何時被創(chuàng)建的,客戶端NioSocketChannel創(chuàng)建過程差不多。

來看ServerBootstrap的bind(...)方法源碼:

## AbstractBootstrap類

public ChannelFuture bind(int inetPort) {
    // 服務端SocketChannel綁定監(jiān)聽端口,格式:0.0.0.0:Port
    return bind(new InetSocketAddress(inetPort));
}

一直點下去,會跟到doBind(...)方法:

## AbstractBootstrap類

private ChannelFuture doBind(final SocketAddress localAddress) {
    // 對channel進行初始化和注冊操作
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }

    ......
}

來看下initAndRegister(...)方法:

## AbstractBootstrap類

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // 創(chuàng)建一個NioServerSocketChannel實例
        channel = channelFactory.newChannel();
        // 初始化channel配置
        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);
    }
    // 服務端channel注冊到bossGroup線程池,由bossGroup線程池來處理accept事件
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

到此,我們知道了服務端的Channel是在AbstractBootstrap類的initAndRegister()方法中創(chuàng)建的

總結

image
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容