零、目錄
一、pipeline整體關系簡述
二、Unsafe的作用
三、事件的分類及處理
四、pipeline中節點的添加和刪除
一、pipeline整體關系簡述
1、netty中的pipeline模型
當EventLoop的selector監聽到某Channel產生了就緒的IO事件,并調用socket API對就緒的IO事件進行操作后,需要將操作產生的“IO數據”或“操作結果”告知用戶進行相應的業務處理。
netty將因外部IO事件導致的Channel狀態變更(Channel被注冊到EventLoop中,Channel狀態變為可用,Channel讀取到IO數據...)或Channel內部邏輯操作(添加ChannelHandler...)抽象為不同的回調事件,并定義了pipeline對Channel的回調事件進行流式的響應處理。
用戶可在pipeline中添加多個事件處理器(ChannelHandler),并通過實現ChannelHandler中定義的方法,對回調事件進行定制化的業務處理。ChannelHandler也可以調用自身方法對Channel本身進行操作。
netty會保證“回調事件在ChannelHandler之間的流轉”及“Channel內部IO操作”由EventLoop線程串行執行,用戶也可以在ChannelHandler中使用自行構建的業務線程進行業務處理。
2、pipeline相關類的關系圖
1)DefaultChannelPipeline:
事件處理流,是一個雙向鏈表結構,鏈表中節點元素為ChannelHandlerContext。
新的AbstractNioChannel創建時,會創建該Channel對應的DefaultChannelPipeline,用于處理該Channel對應的回調事件。
DefaultChannelPipeline創建時,會自動創建并向鏈表中添加兩個ChannelhandlerContext節點——head和tail。
pipeline的fireXXX()方法:回調事件的發起方法。會產生相應回調事件并直接調用ChannelHandlerContext.invokeXXX(head)方法將回調事件傳遞給pipeline的head節點。
2)ChannelHandlerContext:
事件處理器上下文,pipeline中的實際處理節點。
每個處理節點ChannelHandlerContext中包含一個具體的事件處理器ChannelHandler,同時ChannelHandlerContext中也綁定了對應的pipeline和Channel的信息,方便ChannelHandler進行調用。
AbstractChannelHandlerContext具體實現了ChannelHandlerContext接口的功能,并進行了相應擴展。
ChannelHandlerContext的fireXXX方法:回調事件的發起方法。會產生相應回調事件并將其交給pipeline中的下一個處理節點。此方法提供給用戶實現的ChannelHandler使用,用于將回調事件向pipeline中的下一個節點傳遞。
AbstractChannelHandlerContext的static invokeXXX(AbstractChannelHandlerContext next)方法:封裝next.invokeXXX()的邏輯并交給EventLoop的IO線程執行。
ChannelHandlerContext的invokeXXX()方法:回調事件執行方法。執行節點中事件處理器ChannelHandler的XXX方法,實際處理回調事件。
3)ChannelHandler:
ChannelHandler(事件處理器接口),由ChannelInboundHandler接口和ChannelOutboundHandler接口繼承。
ChannelInboundHandler中定義了各個回調事件的回調方法,由用戶進行具體實現。
ChannelOutboundHandler中定義了方法進行Channel內部IO操作(Channel發起bind/connect/close操作,Channel監聽OP_READ,Channel寫IO數據...),供用戶在回調方法中使用。
ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter為接口的默認實現類(其實沒干什么事),用戶通過繼承這兩個類來實現自己的業務處理邏輯。
二、Unsafe的作用
1、概述
Unsafe是Channel的內部類,一個Channel對應一個Unsafe。
Unsafe用于處理Channel對應網絡IO的底層操作。ChannelHandler處理回調事件時產生的相關網絡IO操作最終也會委托給Unsafe執行。
2、類繼承結構
Unsafe接口中定義了socket相關操作,包括SocketAddress獲取、selector注冊、網卡端口綁定、socket建連與斷連、socket寫數據。這些操作都和jdk底層socket相關。
NioUnsafe在Unsafe基礎上增加了幾個操作,包括訪問jdk的SelectableChannel、socket讀數據等。
NioByteUnsafe實現了與socket連接的字節數據讀取相關的操作。
NioMessageUnsafe實現了與新連接建立相關的操作。
3、讀/寫操作實現淺述
三、事件的分類及處理
1、Inbound事件
用于描述因外部事件導致的Channel狀態變更。
ChannelInboundHandler中定義了各個Inbound事件的回調方法。
2、Outbound事件
用于定義Channel能夠提供的IO操作。
ChannelOutboundHandler中定義了Channel提供的進行IO操作的方法。
3、發起并處理inbound事件
ChannelInboundInvoker接口定義了inbound事件的發起方法fireXXX(),用于發起一個inbound事件。
ChannelPipeline接口繼承了ChannelInboundInvoker,其實現類DefaultChannelPipeline因此具有發起inbound事件的功能。-->? 實際通過調用AbstractChannelHandlerContext的靜態invokeXXX()方法,產生并從pipeline的head節點開始接收事件。
ChannelHandlerContext接口繼承了ChannelInboundInvoker,其實現類AbstractChannelhandlerContext因此具有發起inbound事件的功能。 -->? 實際通過調用AbstractChannelHandlerContext的靜態invokeXXX()方法,從當前節點的下一個節點開始接收事件。
AbstractChannelHandlerContext類中定義了靜態invokeXXX(next)方法,用于在指定節點上開始接收inbound事件,并保證接收過程在EventLoop線程中執行。
AbstractChannelHandlerContext類中定義了invokeXXX()方法,用于實際接收inbound事件,并執行其ChannelHandler實現的事件回調方法。
對于inbound事件,因為需要進行業務邏輯處理,因此pipeline的head節點會執行fireXXX()方法將事件透傳給后面的用戶自己實現inbound處理節點,由用戶自己實現的ChannelHandler接收事件并回調執行業務邏輯。
fireChannelActive、fireChannelRead、fireChannelReadComplete等inbound事件的處理過程可以自己跟代碼看一下。(head節點對channelRead事件向后透傳。對channelActive和channelReadComplete事件,向后傳遞并產生了Channel.read()的outbound事件)
4、發起并處理outbound事件
ChannelOutboundInvoker接口定義了outbound事件的產生方法(register,bind,connect,read,write,flush...),用于描述Channel能夠提供的IO操作。
Channel接口繼承了ChannelOutboundInvoker,其實現類AbstractChannel因此具有執行outbound事件的功能。-->? 實際通過調用ChannelPipeline的對應方法實現。
ChannelPipeline接口繼承了ChannelOutboundInvoker,其實現類DefaultChannelPipeline因此具有執行outbound事件的功能。-->? 實際通過調用pipeline的tail節點的對應方法實現。
ChannelHandlerContext接口繼承了ChannelOutboundInvoker,其實現類AbstractChannelhandlerContext因此具有執行outbound事件的功能。-->? 實際從當前節點的前一個outbound處理節點開始接收事件,調用其invokeXXX()方法,并保證接收過程在EventLoop線程中執行。
AbstractChannelHandlerContext類中定義了invokeXXX()方法,用于實際接收outbound事件,并執行其ChannelHandler實現的事件執行方法。
對于outbound事件,因為和IO操作相關,最后會由pipeline中的head節點接收處理。head節點實現了ChannelHandler的事件執行方法,將實際的執行操作委托給Unsafe進行。
bind、connect、read、writeAndFlush等outbound事件的處理過程可以自己跟代碼看一下。(最終由head節點委托給Unsafe類執行相關IO操作)
5、head、tail節點的作用
head節點:
head節點既是inBound處理節點,又是outBound處理節點。
head節點作為pipeline的頭結點開始接收并傳遞inbound事件。并作為pipeline的最后一環最終接收處理outbound事件(委托Unsafe進行outbound事件的相關IO操作)。
tail節點:
tail節點是inBound處理節點。
tail節點作為pipeline的第一環傳遞outbound事件,其實就是將outbound事件透傳到前一個outbound處理節點。并作為pipeline的最后一環最終接收inbound事件,大部分左右是終止inbound事件的傳播。
tail節點的exceptionCaught方法:若最終在用戶自定義的處理節點沒有捕獲處理異常,則在tail節點捕獲異常打印警告日志。
tail節點的channelRead方法:若Channel讀入的ByteBuf在流經pipeline過程中沒有被消費掉,最終流入了tail節點,則將該ByteBuf丟棄回收并打印警告日志。
四、pipeline中節點的添加和刪除
1、向pipeline中添加ChannelHandler的過程
從DefaultChannelPipeline.addLast(ChannelHandler... handlers)這行代碼開始跟吧
細節不想寫了,列一下大致流程:
1)checkMultiplicity(handler);? -->? 檢查要添加的handler是否被重復添加
2)newCtx= newContext(group,filterName(name,handler),handler);? -->? 創建節點
3)addLast0(newCtx);? --> 添加節點到pipeline的末尾(tail之前)
4)callHandlerAdded0(newCtx);? -->? 回調新建節點的handlerAdded(ctx)方法
2、將ChannelHandler從pipeline中移除的過程
從DefaultChannelPipeline.remove(ChannelHandler handler)這行代碼開始跟吧
細節不想寫了,列一下大致流程:
1)getContextOrDie(handler)? -->? 遍歷pipeline鏈表找到待刪除的接待對象
2)remove0(ctx)? --> 將節點從pipeline鏈表中移除
3)callHandlerRemoved0(ctx)? ? --> 回調移除節點的handlerRemoved(ctx)方法。