netty系列之:netty中各不同種類的channel詳解

flydean發表於2022-03-16

簡介

channel是連線客戶端和伺服器端的橋樑,在netty中我們最常用的就是NIO,一般和NioEventLoopGroup配套使用的就是NioServerSocketChannel和NioSocketChannel,如果是UDP協議,那麼配套使用的就是NioDatagramChannel,如果是別的協議還有其他不同的Channel型別。

這些不同channel型別有什麼區別呢?一個直觀的感覺就是不同的channel和channel連線使用的協議有關係,不同的channel可能適配了不同的連線協議。

事實到底是不是如此呢?在netty的內部實現中到底有多少種channel呢?今天一起來探討一下。

ServerChannel和它的型別

雖然ServerChannel繼承自Channel,但是ServerChannel本身並沒有新增任何新的方法:

public interface ServerChannel extends Channel {

}

所以對ServerChannel和Channel來說都可以看做是Channel,他們只是語義上有區別。

但是因為ServerChannel繼承自Channel,所以相對的ServerChannel的分類和實現要比Channel要少。所以我們先以ServerChannel為例進行講解。

ServerChannel的實現也有很多,我們以Abstract*開頭的實現為例,下面是他們的繼承關係:

netty系列之:netty中各不同種類的channel詳解

從上圖我們可以看出,ServerChannel有六個抽象類實現,分別是AbstractEpollServerChannel,AbstractKQueueServerChannel,AbstractServerChannel,ServerSocketChannel,SctpServerChannel和ServerDomainSocketChannel。

其中前面三個抽象類同時繼承自AbstractChannel。

Epoll和Kqueue

Epoll和Kqueue是兩個獨特的依賴於特定平臺的NIO協議,其中epoll只在linux平臺才支援,而kQueue則在FreeBSD、NetBSD、OpenBSD、macOS 等作業系統支援。

我們來看下AbstractEpollServerChannel的建構函式:

    protected AbstractEpollServerChannel(int fd) {
        this(new LinuxSocket(fd), false);
    }

    AbstractEpollServerChannel(LinuxSocket fd) {
        this(fd, isSoErrorZero(fd));
    }

    AbstractEpollServerChannel(LinuxSocket fd, boolean active) {
        super(null, fd, active);
    }

所有的建構函式都需要一個LinuxSocket的引數,LinuxSocket是一個socket用來提供對於linux native方法的訪問支援。

同樣的,我們再看一下AbstractKQueueServerChannel的建構函式:

    AbstractKQueueServerChannel(BsdSocket fd) {
        this(fd, isSoErrorZero(fd));
    }

    AbstractKQueueServerChannel(BsdSocket fd, boolean active) {
        super(null, fd, active);
    }

AbstractKQueueServerChannel的建構函式需要傳入一個BsdSocket引數,BsdSocket是一個類用來提供對BSD系統的本地方法的訪問。

AbstractServerChannel

AbstractServerChannel我們在之前的channel一章中已經講過了,它的唯一實現就是LocalServerChannel,用於本地的transport。

ServerSocketChannel

ServerSocketChannel是一個以Socket連線為基礎的ServerChannel,既然是Socket連線,那麼ServerSocketChannel中提供了一個InetSocketAddress型別的localAddress和一個remoteAddress, 另外還有一個ServerSocketChannelConfig屬性,用來儲存ServerSocketChannel相關的配置資訊:

public interface ServerSocketChannel extends ServerChannel {
    @Override
    ServerSocketChannelConfig config();
    @Override
    InetSocketAddress localAddress();
    @Override
    InetSocketAddress remoteAddress();
}

ServerDomainSocketChannel

ServerDomainSocketChannel是使用DomainSocket來進行通訊的ServerChannel。什麼是DomainSocket呢?

DomainSocket的全稱是unix domain socket,它又可以叫做IPC socket,也就是inter-process communication socket,是在unix平臺上的同一伺服器上的程式通訊方式。

我們知道,協議是比較複雜的,對於傳統的socket通訊來說,需要定製特定的協議,然後進行封包和解包等操作,但是使用DomainSocket,可以直接將程式的資料直接拷貝,從而節約了時間,並提高了程式的效率。

DomainSocket的地址是一個檔案的路徑,實際上是下面的一個結構體:

struct sockaddr_un {
    sa_family_t     sun_family;     /* AF_UNIX ,2位元組*/
    char    sun_path[UNIX_PATH_MAX];        /* 路徑名 */
};

在ServerDomainSocketChannel中的remoteAddress和localAddress的型別都是DomainSocketAddress,DomainSocketAddress有一個socketPath屬性,用來儲存DomainSocket檔案的路徑。

SctpServerChannel

最後一個要講解的ServerChannel是SctpServerChannel,Sctp的全稱是Stream Control Transmission Protocol,他是一種類似於TCP/IP的協議。和SocketServerChannel一樣,SctpServerChannel中也有一個config叫做SctpServerChannelConfig,還提供了多個bindAddress方法用來繫結InetAddress.

有關Sctp協議的具體內容,本章不深入討論,感興趣的朋友可以關注後續的章節。

Channel和它的型別

Channel作為ServerChannel的父類,又有哪些實現呢?

先來看下常用channel的實現類:

netty系列之:netty中各不同種類的channel詳解

看起來channel的實現類非常多,基本上都是按照channel中使用傳輸協議的型別來的。

我們具體來看一下相應的實現類。

UnixChannel

UnixChannel表示的unix平臺上的操作,它有一個fd方法,返回一個FileDescriptor:

FileDescriptor fd();

這也是unix和windows平臺的區別之一,unix平臺所有的一切都可以用檔案來表示。

SctpChannel

在上面我講SctpServerChannel的時候我們提過了,Sctp是一個類似於tcp/ip的協議,SctpChannel中定義了協議中需要使用到的localAddress和remoteAddress:

InetSocketAddress localAddress();

InetSocketAddress remoteAddress();

同時還定義了一些繫結方法:

    ChannelFuture bindAddress(InetAddress var1);

    ChannelFuture bindAddress(InetAddress var1, ChannelPromise var2);

    ChannelFuture unbindAddress(InetAddress var1);

    ChannelFuture unbindAddress(InetAddress var1, ChannelPromise var2);

DatagramChannel

DatagramChannel用來處理UDP協議的連線,因為UDP有廣播的功能,所以DatagramChannel中提供了joinGroup的方法,來join一個multicast group:

ChannelFuture joinGroup(InetAddress multicastAddress);

當然,可以join就可以leave,還有一些leaveGroup的方法:

ChannelFuture leaveGroup(InetAddress multicastAddress);

還可以block某些地址在給定的networkInterface上的廣播:

ChannelFuture block(
            InetAddress multicastAddress, NetworkInterface networkInterface,
            InetAddress sourceToBlock);

這些方法都和UDP的特性是息息相關的。

DomainDatagramChannel

DomainDatagramChannel和之前提到的ServerDomainSocketChannel一樣,都是使用的IPC內部程式通訊技術,直接進行程式的拷貝,免去了協議解析等步驟,提升了處理速度。

DuplexChannel

DuplexChannel從名字看就是一個雙向的channel,duplex Channel有一個特點,就是channel的兩邊可以獨立的關閉,所以有下面的方法:

boolean isInputShutdown();

ChannelFuture shutdownInput();

boolean isOutputShutdown();

ChannelFuture shutdownOutput();

DuplexChannel的是實現有很多種,比如常見的NIOSocketChannel,KQueueSocketChannel,EpollSocketChannel等。

AbstractChannel

另外一個channel的非常重要的子類就是AbstractChannel,AbstractChannel有三個非常重要的實現,分別是AbstractNioChannel,AbstractKQueueChannel和AbstractEpollChannel。

這三個類使用的都是NIO技術,不同的是第一個使用的是select,後面兩個使用的是平臺獨有的KQueue和Epoll技術。

其中NIO又可以分為NioByteChannel和NioMessageChannel,KQueue和Epoll又可以分為StreamChannel和DatagramChannel。

總結

以上就是channel在netty中的基本實現和分類。後面我們會詳解講解具體的channel到底是如何實現的。

本文已收錄於 www.flydean.com

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!

相關文章