在Netty上百炬,Pipeline
把ChannelHandler
串聯(lián)在一起來組織處理邏輯。比如實現(xiàn)協(xié)議棧HTTP
,HTTP2
污它。而ChannelHandlerContext
可以認為是Pipeline
用于串聯(lián)ChannelHandler
的紐帶剖踊。開發(fā)者的業(yè)務(wù)邏輯基本上是在ChannelHandler
實現(xiàn)的,理解這三者以及三者之間的關(guān)聯(lián)是使用Netty構(gòu)建模塊化衫贬、可復(fù)用程序的關(guān)鍵蜜宪。
ChannelHandler
上圖是
ChannelHandler
的類結(jié)構(gòu)圖,在此總結(jié)下ChannelHandler
的功能祥山。
- 響應(yīng)
ChannelHandler
狀態(tài)變化 - 響應(yīng)與其關(guān)聯(lián)的
Channel
生命周期內(nèi)的狀態(tài)變化以及處理接收的數(shù)據(jù) - 處理與其關(guān)聯(lián)的
Channel
上的Outbound
操作
下面針對這三個方面詳細說明。
Channel的生命周期
ChannelInboundHandler
ChannelInboundHandler
的方法與其緊密對應(yīng)掉伏。
-
channelRegistered
Channel 注冊到其EventLoop上缝呕,此時就可以處理I/O了澳窑。 -
channelActive
對于NioSocketChannel
其active狀態(tài)是如下定義:
public boolean isActive() {
SocketChannel ch = javaChannel();
return ch.isOpen() && ch.isConnected();
}
對于NioServerSocketChannel
@Override
public boolean isActive() {
return javaChannel().socket().isBound();
}
-
channelRead
Channel讀取數(shù)據(jù)后觸發(fā) -
channelReadComplete
一次讀取周期完成,從下面的讀取邏輯可以看出供常,channelReadComplete的在Channel讀緩沖區(qū)讀取完時觸發(fā)摊聋。
try {
do {
//RecvByteBufAllocator 默認使用AdaptiveRecvByteBufAllocator,其根據(jù)上一次讀取的字節(jié)數(shù)動態(tài)調(diào)整本次讀取字節(jié)數(shù)
byteBuf = allocHandle.allocate(allocator);
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
/**
* 繼續(xù)讀取的邏輯:
* config.isAutoRead() &&
attemptedBytesRead == lastBytesRead &&
totalMessages < maxMessagePerRead(默認為1) &&
totalBytesRead < Integer.MAX_VALUE;
* */
} while (allocHandle.continueReading());
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
-
channelWritabilityChanged
當Channel的可寫狀態(tài)發(fā)生變化時觸發(fā)栈暇。當channel不可寫時用戶可以用此來控制數(shù)據(jù)發(fā)送不至于太快麻裁,當Channel可寫時用戶可以繼續(xù)發(fā)送數(shù)據(jù)。Channel的可寫性可以由用戶來進行設(shè)置源祈〖逶矗可以把此理解為用戶空間的寫緩存,和socket的系統(tǒng)緩存無關(guān)香缺。
Channel.config().setWriteHighWaterMark()
Channel.config().setWriteLowWaterMark().
ChannelOutboundHandler
ChannelOutboundHandler
給用戶機會來進一步處理用戶在Channel上的操作手销。一些比較典型的應(yīng)用場景是filter掉一些操作,即根據(jù)需求拒絕一些操作图张;Socket面向字節(jié)流锋拖,在實現(xiàn)具體 的協(xié)議比如Http時,可以讓用戶只處理自己關(guān)心的數(shù)據(jù)祸轮,OutboundHandler可以把這些數(shù)據(jù)封裝成協(xié)議需要的數(shù)據(jù)兽埃,然后交給socket發(fā)送。
method | 描述 |
---|---|
bind(ChannelHandlerContext,SocketAddress,ChannelPromise) | |
connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise) | |
disconnect(ChannelHandlerContext,ChannelPromise) | |
close(ChannelHandlerContext,ChannelPromise) | |
deregister(ChannelHandlerContext,ChannelPromise) | |
flush(ChannelHandlerContext) | |
write(ChannelHandlerContext,ChannelPromise) | 适袜。柄错。。痪蝇。 |
ChannelPipeline
上圖基本上可以概括Socket 和 ChannelPipeline
以及ChannelHandler
和ChannelPipeline
之間的關(guān)系鄙陡。
-
ChannelPipeline
把ChannelHandler
串聯(lián)在一起來攔截處理Channel產(chǎn)生的inbound和outbound事件,這些ChannelHandler構(gòu)成了應(yīng)用程序的數(shù)據(jù)和事件處理邏輯躏啰。 - 每個Channel會與唯一一個
ChannelPipeline
實例進行綁定趁矾。 - inbound事件的流向Head -> Tail,
-
outbound事件的流向是Tail->Head给僵,這有點像網(wǎng)絡(luò)協(xié)議棧毫捣,而且Netty本身實現(xiàn)的協(xié)議比如Http,Http2都是基于ChannelHandler帝际,在其上實現(xiàn)數(shù)據(jù)的解碼與編碼蔓同。我的理解是
ChannelPipeline
把用戶的邏輯同socket關(guān)聯(lián)起來,我覺著其上面最適合實現(xiàn)的邏輯應(yīng)該是協(xié)議的實現(xiàn)蹲诀,至于用戶的業(yè)務(wù)邏輯不應(yīng)該在ChannelHandler上實現(xiàn)斑粱。
ChannelPipeline提供的method
ChannelPipeline提供了大量的方法,可以分為三類脯爪,
- 觸發(fā)outbound事件的方法则北;
- 觸發(fā)inbound事件的方法
- 對Pipeline上Handler以及Context進行管理的方法
ChannelHandlerContext
ChannelPipeline
并不是直接把ChannelHandler
串聯(lián)起來的矿微,而是通過ChannelHandlerContext
∩写В可以認為ChannelPipeline
是雙向鏈表涌矢, 而鏈表上的節(jié)點就是ChannelHandlerContext
。因此快骗,handler的處理完后的事件實際是通過ChannelHandlerContext
傳遞給下一個Handler的娜庇。
上圖是ChannelHandlerContext的類圖,從這里可以看出其提供的方法同ChannelPipeline
類似方篮,但是與前者不同的是名秀,前者觸發(fā)的事件被整個Pipeline處理,而Context觸發(fā)的事件僅從下一個handler開始處理恭取。如下圖所示:
總結(jié)
- 每個
ChannelHandler
實例唯一關(guān)聯(lián)一個ChannelHandlerContext
實例 -
ChannelPipeline
通過ChannelHandlerContext
把ChannelHandler
串聯(lián)成一個雙向鏈表結(jié)構(gòu)泰偿,用來處理Channel產(chǎn)生的inbound和Outbound事件。這種處理方式和網(wǎng)絡(luò)協(xié)議棧的實現(xiàn)方式很像蜈垮,因此我覺著特別適合基于Netty實現(xiàn)各種應(yīng)用協(xié)議耗跛。而Pipeline就是協(xié)議的編碼和解碼的實現(xiàn)邏輯。 - 個人見解攒发,
ChannelHandler
的實現(xiàn)最好只與協(xié)議的編碼和解碼有關(guān)调塌,與具體業(yè)務(wù)相關(guān)的邏輯不是很適合在其上進行實現(xiàn)。