Java Netty 學習(八)
在普通IO
中,通過把機器傳輸抽象成java.net.Socket
流,也就是Socket
和ServerSocket
到NIO
時,則變為了java.nio.channels.Channel
,也是作為傳輸的管道,具體可看Java Netty 學習(四) - NIO基礎知識Channel和Pipe
那今天就一起學習Netty中的Channel,看它又是一種怎麼樣的設計形式。
介紹
在netty的Channel中,提供了很多共用的API,相比與大大降低了直接使用Socket
的複雜性。另外,它是Netty網路通訊的主題,由他負責直接對端進行網路通訊,註冊,以及相關的資料操作。
Channel提供了許多預定義的專門實現的類:
- EmbeddedChannel:用於測試ChannelHandler的類
- LocalServerChannel:同一個JVM內部實現client和server之間通訊的Channel
- NioDatagramChannel:使用UDP進行網路傳輸,和java.net.DatagramSocket對應
- NioSctpChannel:基於Sctp協議的操作
- NioSocketChannel:使用TCP進行網路傳輸
結構
Channel接口裡面主要有以下方法:
- ChannelId id():返回唯一的channel id
- EventLoop eventLoop():返回專屬被註冊進入的EventLoop
- Channel parent():返回父channel
- ChannelConfig config():返回channel的config
- SocketAddress localAddress():返回自己的socketAddress
- SocketAddress remoteAddress():返回遠端的Address
- Unsafe unsafe():返回unsafe,內部實現的unsafe,一些方法只能用於I/O執行緒呼叫,其他的如localAddress則可以被使用者執行緒呼叫。
狀態
按照Channel定義的模型,Channel有四個狀態
- ChannelUnregistered:Channel已經被建立,但還未註冊到EventLoop
- ChannelRegistered:Channel已經被註冊到EventLoop
- ChannelActive:Channel處於活動狀態,他現在可以接受和傳送資料了
- ChannelInactive:Channel沒有連線到遠端節點
NioSocketChannel
下面看看NioSocketChannel相關特性:
賦值
記得上一篇文章給出的例項,Bootstrap呼叫的建造者模式的過程中用到了NioSocketChannel
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HelloClientHandler());
}
});
下面跟著原始碼看看裡面做了些什麼事:
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
再看channelFactory:
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
最後設定以下channelFactory:
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 (B) this;
}
在channel方法裡面,首先還是返回一個AbstractBootstrap
的子類以供繼續建造物件,接著把一個
ReflectiveChannelFactory
物件傳遞給了channelFactory
,並且以傳入的channelClass為引數,名字上可知是返回一個channel類,包裝了很多層工廠方法為了實現程式碼的統一性。
進入原始碼可知,ReflectiveChannelFactory
是一個繼承了ChannelFactory
的物件裡面有一個newChannel
方法如下:
@Override
public T newChannel() {
try {
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
執行了傳入引數的Class的newInstance
方法。
現在已經清楚知道Bootstrap中的channel方法做了什麼事了,就是把一個相應型別的ChannelFactory傳進去,這樣就可以產生Channel。
那麼,NioSocketChannel什麼時候被初始化的呢?
初始化
首先在NioSocketChannel中打斷點,跟蹤到呼叫服務:
可以很清楚的看到
由connect->doResolveAndConnect->initAndRegister->newChannel一級一級呼叫:
返回一個非同步的ChannelFuture
ChannelFuture future = b.connect(this.address, this.port);
再到:
public ChannelFuture connect(String inetHost, int inetPort) {
return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
}
接著在doResolveAndConnect
執行初始化一個Channel,但此時並沒有連線上,就相當先拿到一個預設配置的Channel,但是現在還沒有用。
接著執行initAndRegister
,並在這裡面呼叫:
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);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
從而再經過JNI等一步步反射呼叫NioSocketChannel的構造方法。
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
}
用途
那麼,Channel建立了,也初始化了,那麼它在Netty裡面,地位是啥呢?
先看看一張圖:
上圖有以下幾個概念:
- 客戶端連線成功,就新建一個Channel來接受(程式碼中是先初始化一個Channel,連線成功後賦值屬性)
- EventLoopGroup分配一個EventLoop給Channel,並註冊到該EventLoop,channel生命週期內都和該EventLoop在一起(不變)同時,註冊時獲得selectionKey
- Channel進行網路連線、關閉和讀寫,生成相對應的event(改變selectinKey資訊),觸發eventloop排程執行緒進行執行
- EventLoop執行排程ChannelPipline來處理使用者業務邏輯
並且,多個Channel可以同時註冊到同一個EventLoop,並按照順序迴圈處理Channel中的邏輯。
總結
Channel就類似於一個容器,或者理解為Socket,通過它與另一端進行通訊,由EventLoop來處理Channel中產生的事件,在事件中實現相關邏輯,接下來文章將逐步學習其他元件。
幫助: