Netty之Pipeline的原理和事件傳播機制
文章目錄
一、包含知識點
- ChannelPipeline初始化流程
- ChannelPipeline初始化流程
- ChannelInitializer添加
- 自定義ChannelHandler添加入隊列
- Pipeline事件傳播機制
二、 Channel和Pipeline之間關係
本篇文章主要講解Netty中Pipeline相關的知識點 , 而pipeline和Channel有着緊密的聯繫, 數據的讀取、寫入都需要經過Channel,而Pipeline被綁定到了Channel, 那麼從哪裏可以知道Channel和Pipeline之間有關係呢 ?
常見的Channel有NioServerSocketChannel、NioSocketChannel, 這裏我們看下NioServerSocketChannel類繼承關係, 從類繼承關係 NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel 進行查找, 在AbstractChannel抽象類中我們找到了pipeline變量, 這說明了Channel和Pipeline是有相互關係的
//AbstractChannel
private final DefaultChannelPipeline pipeline;
三、ChannelPipeline初始化流程
3.1 Channel初始化流程
在第二節中我們知道Channel和Pipeline之間存在聯繫, 那麼Pipeline的初始化是否和Channel初始化有關係呢?帶着這個疑問, 先查看抽象類AbstractChannel, 在構造方法中看到下面的代碼, pipeline是在構造函數中創建的, 而構造函數在初始化的時候纔會調用, 也就是Channel初始化的時候會同時創建pipeline對象。
//AbstractChannel
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
protected AbstractChannel(Channel parent, ChannelId id) {
this.parent = parent;
this.id = id;
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
在上篇 Netty 核心原理之運行機制 文章中我們提到了Channel創建方式,服務啓動(bind、connect)的時候會執行initAndRegister()方法, 該方法會執行channelFactory.newChannel()創建具體的Channel, 這裏以NioServerSocketChannel爲例說明
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
/**
* Channel對象的創建, 來自下面語句
* channel(NioServerSocketChannel.class)
*/
channel = channelFactory.newChannel(); // 創建Channel對象, 這裏是 NioServerSocketChannel
init(channel); // 對Channel對象進行初始化
} catch (Throwable t) {
//... 省略部分其它代碼
}
//... 省略部分其它代碼
return regFuture;
}
Channel構造方法初始化調用流程如下, 從構造函數初始化流程在AbstractChannel抽象類中找到了pipeline創建邏輯, 和本小節開始時分析是一致的。
//NioServerSocketChannel
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
// AbstractNioMessageChannel
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
// AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
//...省略其它部分代碼
}
//AbstractChannel
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
3.2 Pipeline實例化
pipeline實例化是調用內部方法newChannelPipeline創建的
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
DefaultChannelPipeline創建的時候做了什麼事情呢? 看下下面的代碼
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
可以看到, 入參channel賦值給了this.channel, 同時創建了兩個字段tail、head,這兩個字段維護了以AbstractChannelHandlerContext爲節點的雙向鏈表, 分別表示隊尾、隊頭,這個雙向鏈表是Netty實現Pipeline機制的關鍵, 下面分別看下HeadContext、TailContext類結構圖
從類結構圖可以看出, HeadContext、TailContext主要相似和區別是
- 差異
- HeadContext實現了ChannelOutboundHandler(雖然也實現了ChannelInboundHandler)
- TailContext實現了ChannelInboundHandler
- 相似
- 都實現了ChannelHandler、ChannelHandlerContext, 可以說head、tail既是一個ChannelHandler, 也是ChannelHandlerContext
從上面類繼承關係我們知道HeadContext、TailContext的差異是ChannelOutboundHandler、ChannelInboundHandler, 那從哪裏可以直觀的這種區別呢 ? 我們看下HeadContext、TailContext構造方法
//HeadContext
HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, HEAD_NAME, false, true);
unsafe = pipeline.channel().unsafe();
setAddComplete();
}
//TailContext
TailContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, TAIL_NAME, true, false);
setAddComplete();
}
從構造方法中, 主要差別是super調用時入參的區別
- name的區別, 這點事顯然的爲了區別隊列的頭和尾
- inbound、outbound的區別
- head: inbound – false ,outbound – true
- tail: inbound – true ,outbound – false
3.3 Pipeline實例化後圖示
四、ChannelInitializer添加
4.1 init添加自定義handler、childHandler
首先我們看下ChannelInitializer的使用代碼示例
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new new LengthFieldBasedFrameDecoder())
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//對象參數類型編碼器
pipeline.addLast("encoder",new ObjectEncoder());
//對象參數類型解碼器
pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,ClassResolvers.cacheDisabled(null)));
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
在第三節中,DefaultChannelPipeline創建後, 並不能實現什麼功能, 因爲並沒有給他添加自定義的ChannelHandler, 而自定義功能是在childHandler或handler裏面, 通過自定義實現的ChannelInitializer#initChannel方法添加自定義handler, 那麼ChannelInitializer是什麼時候添加到ChannelPipeline的呢?在3.1節中,我們提到了initAndRegister()方法, 查看對應代碼, 在創建Channel之後還有init()方法,查看該方法代碼
//ServerBootstrap
void init(Channel channel) throws Exception {
// ...省略部分其它代碼
ChannelPipeline p = channel.pipeline();
// ...
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler(); //初始化時 .handler() 對應邏輯
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() { //初始化時 .childHandler() 對應邏輯
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
//... 省略部分其它代碼
}
});
}
init方法初始化相關信息的時候, 通過ChannelInitializer將handler、chiledHandler中添加的自定義handler添加到pipeline隊列中(這裏是以Server端init方法舉例),從源碼中可以看出存在handler時纔會進行添加,而childHandler會被封裝成ServerBootstrapAcceptor再進行添加,如果是Client則直接對handler進行添加, 那麼handler、childHandler有什麼區別呢 ?
- handler:
- 在Server、Client端都存在對應實現
- 初始化的時候,如果handler不爲空, 會執行相關handler
- 用於監聽Channel各種動作以及狀態的改變, 包括連接、綁定、接收消息等
- childHandler
- 只在Server端存在
- 在客戶端成功連接後才執行, 源碼中chidlHandler是在自定義線程中被封裝成ServerBootstrapAcceptor再添加到pipeline
- 用於監聽已連接客戶端的Channel動作和狀態
4.2 handler轉ChannelHandlerContext
在上一節中,我們知道通過addLast方法添加的參數是ChannelHandler, 而隊列維護的元素是AbstractChannelHandlerContext, 那麼ChannelHandler是怎麼轉換爲AbstractChannelHandlerContext的呢 ?查看addLast()核心方法
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler); // 檢查handler是否重複
newCtx = newContext(group, filterName(name, handler), handler); // 創建DefaultChannelHandlerContext
addLast0(newCtx); //將Context添加到隊列尾部
}
// 省略部分其它代碼
return this;
}
該構造方法主要做了下面幾個事情
- 爲了避免併發問題, 用synchronized同步鎖進行修飾
- checkMultiplicity()方法檢測handler是否重複, 如果重複會拋ChannelPipelineException異常
- newContext()方法將handler封裝成AbstractChannelHandlerContext
- addLast0()方法將新創建的newCtx添加到隊列中
newContxt()方法具體幹什麼了呢 ? 查詢代碼
//DefaultChannelPipeline
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
//DefaultChannelHandlerContext
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
}
在構造方法DefaultChannelHandlerContext調用super操作時, 調用了isInbound、isOutnound方法,下面是兩個方法的實現邏輯
private static boolean isInbound(ChannelHandler handler) {
return handler instanceof ChannelInboundHandler;
}
private static boolean isOutbound(ChannelHandler handler) {
return handler instanceof ChannelOutboundHandler;
}
從實現邏輯可以看出, isInbound、isOutbound分別是以ChannelInboundHandler、ChannelOutboundHandler來確定的, 如果handler繼承了ChannelInboundHandler則爲inbound、如果繼承了ChannelOutboundHandler則爲outBound
- ChannelInboundHandler
- ZlibDecoder
- LineBasedFrameDecoder
- ObjectDecoder
- ChannelOutboundHandler
- Bzip2Encoder
- ObjectEncoder
- LengthFieldPrepender
通過newCtx方法創建完Context後, 執行addLast0方法進行入隊操作, 下面是相關邏輯代碼,在保持HeadContext、TailContext隊頭、隊尾不變的情況下, 將新添加的節點newCtx作爲隊列的尾部。
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
4.3 init之後pipeline隊列情況
五、自定義ChannelHandler添加入隊列
5.1 自定義ChannelHandler添加流程
第四節中我們分析了child、childHandler中ChannelInitiallzer添加入隊列的流程, ChannelInitiallzer中定義的自定義ChannelHandler又是怎麼添加到隊列中的呢 ?本小節對這個做分析,首先我們再次看下initAndRegister方法
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel(); //創建channel
init(channel); // 初始化channel
} catch (Throwable t) {
// 省略部分代碼
}
ChannelFuture regFuture = config().group().register(channel); // 將channel進行註冊到selector
//省略部分代碼
return regFuture;
}
前面我們已經分析過newChannel()、init()相關方法對應邏輯,只有handler、childHandler對應ChannelInitializer添加涉及到pipeline邏輯,沒有涉及到自定義ChannelHandler添加入隊列邏輯, 繼續查看initAndRegister方法, 其中有下面部分代碼
ChannelFuture regFuture = config().group().register(channel);
這部分代碼在 Netty 核心原理之運行機制 已經分析過, 這裏不再分析,直接跟蹤代碼到如下register0代碼,代碼塊中,只保留了需要分析的部分邏輯代碼
//AbstractChannel
private void register0(ChannelPromise promise) {
try {
// 省略部分代碼
pipeline.fireChannelRegistered();
// 省略部分代碼
} catch (Throwable t) {
// 省略部分代碼
}
}
//DefaultChannelPipeline
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
//AbstractChannelHandlerContext
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
//AbstractChannelHandlerContext
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRegistered();
}
}
// AbstractChannelHandlerContext
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
從代碼中可以看到, 調用靜態方法invokeChannelRegistered時直接將head作爲了入參, 顯然會從head開始遍歷Pipeline雙向鏈表, 找到屬性爲inbound的ChannelHandler, 然後處理對應邏輯, 下面是邏輯調用流程, 其中紅色部分是循環處理邏輯,持續尋找屬性爲inBound的ChannelHandler
AbstractChannel#register0(promise) -> DefaultChannelPipeline#fireChannelRegistered() -> AbstractChannelHandlerContext#invokeChannelRegistered(head) -> **AbstractChannelHandlerContext#invokeChannelRegistered() -> AbstractChannelHandlerContext#fireChannelRegistered() -> AbstractChannelHandlerContext#findContextInbound() **
如果當前inBound是ADD_COMPLETE操作 或 ADD_PENDING且order=false,會調用channelRegistered操作, 初始化之後由4.3 圖示, ChannelHandler就是ChannelInitializer, 實際調用的是ChannelInitializer的channelRegistered方法
//ChannelInitializer
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
if (initChannel(ctx)) {
ctx.pipeline().fireChannelRegistered();
} else {
ctx.fireChannelRegistered();
}
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
try {
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(ctx, cause);
} finally {
remove(ctx);
}
return true;
}
return false;
}
在執行channelRegistered方法時,會執行方法initChannel()進行自定義ChannelHandler添加操作, 執行initChannel方法時,會調用Bootstrap、ServerBootstrap初始化時ChannelInitializer重寫的initChannel(Channel)方法, 即下面邏輯代碼
childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
//自定義協議編碼器
pipeline.addLast(new LengthFieldPrepender(4));
//對象參數類型編碼器
pipeline.addLast("encoder",new ObjectEncoder());
}
})
添加之後會執行finally邏輯代碼塊, 該邏輯主要是將init時添加的ChannelHandler, 即ChannelInitializer進行刪除
//ChannelInitializer
private void remove(ChannelHandlerContext ctx) {
try {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
} finally {
initMap.remove(ctx);
}
}
5.2 自定義ChannelHandler入隊後情況
5.3 ChannelHandler默認命名規則
在執行addLast()進行ChannelHandler添加時, 如果沒有指定handler名稱,會執行filterName()方法創建名稱, 下面是具體的代碼, 從代碼的執行邏輯可以看見, ChannelHandler名字規則是SimpleName + “”#0", 比如: ObjectEncoder -> ObjectEncoder#0
//DefaultChannelPipeline
private String filterName(String name, ChannelHandler handler) {
if (name == null) {
return generateName(handler);
}
checkDuplicateName(name);
return name;
}
private String generateName(ChannelHandler handler) {
Map<Class<?>, String> cache = nameCaches.get();
Class<?> handlerType = handler.getClass();
String name = cache.get(handlerType);
if (name == null) {
name = generateName0(handlerType);
cache.put(handlerType, name);
}
// It's not very likely for a user to put more than one handler of the same type, but make sure to avoid
// any name conflicts. Note that we don't cache the names generated here.
if (context0(name) != null) {
String baseName = name.substring(0, name.length() - 1); // Strip the trailing '0'.
for (int i = 1;; i ++) {
String newName = baseName + i;
if (context0(newName) == null) {
name = newName;
break;
}
}
}
return name;
}
private static String generateName0(Class<?> handlerType) {
return StringUtil.simpleClassName(handlerType) + "#0";
}
六、Pipeline事件傳播機制
6.1 事件傳播機制
在類ChannelPipeline中有下面對事件傳播介紹, 在AbstractChannelHandlerContext中有inbound、outbound兩個boolean變量, 用於標識handler類型
- inbound=true, 表示其對應的ChannelHandler是ChannelInboundHandler子類
- outbound=true,表示其對應的ChannelHandler是ChannelOutboundHandler子類
從事件傳播流程可以看出, inbound、outbound事件流向是不同的
- inbound
- 事件流向是自低向上
- 通過 ChannelHandlerContext.fireIN_EVT() 方法進行傳遞
- outbound
- 事件流向是自頂向下
- 通過 ChannelHandlerContext.OUT_EVT() 方法進行傳遞
6.2 事件傳播分類
事件傳播分爲inbound、outbound,它們分別對應ChannelInboundHandler、ChannelOutboundHandler, 查閱接口, 包含的事件傳播方法包含
- inbound, 事件回調, 響應請求事件
public interface ChannelInboundHandler extends ChannelHandler {
void channelRegistered(ChannelHandlerContext ctx) throws Exception;
void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
void channelActive(ChannelHandlerContext ctx) throws Exception;
void channelInactive(ChannelHandlerContext ctx) throws Exception;
void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
- outbound, 主動觸發, 發起請求事件
public interface ChannelOutboundHandler extends ChannelHandler {
void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
void connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception;
void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
void read(ChannelHandlerContext ctx) throws Exception;
void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
void flush(ChannelHandlerContext ctx) throws Exception;
}
6.3 自定義事件傳播方式
那麼對於捕獲的事件, 如果需要將這個事件傳遞下去, 需要調用 fireChannelActive() 方法
//自己定義的 MyInBoundHandler
public class MyInBoundHandler extends ChannelInboundhandlerAdapter{
public void channelActive(ChannelHandlerContext ctx) {
//業務邏輯處理
ctx.fireChannelActive();
}
}
//AbstractChannelHandlerContext
public ChannelHandlerContext fireChannelActive() {
final AbstractChannelHandlerContext next = findContextInbound();
invokeChannelActive(next);
return this;
}
MyInBoundHandler處理完邏輯後, 會調用fireChannelActive將邏輯傳遞下去, findContextInbound會進行遍歷找到下一個InboundHandler繼續進行邏輯處理
6.4 OutBound事件傳播方式
在6.2節中,我們提到OutBound屬於主動觸發, 發起請求事件, 這裏以Bootstrap的connect爲例進行說明, 跟蹤connect代碼, 核心處理邏輯如下代碼
//AbstractChannel
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
return pipeline.connect(remoteAddress, localAddress, promise);
}
//DefaultChannelPipeline
public final ChannelFuture connect(
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
return tail.connect(remoteAddress, localAddress, promise);
}
//AbstractChannelHandlerContext
public ChannelFuture connect(
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
}
if (!validatePromise(promise, false)) {
// cancelled
return promise;
}
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeConnect(remoteAddress, localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeConnect(remoteAddress, localAddress, promise);
}
}, promise, null);
}
return promise;
}
//AbstractChannelHandlerContext
private AbstractChannelHandlerContext findContextOutbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
從代碼可以看出, outBound事件傳播是從隊列尾部開始的, 在AbstractChannelHandlerContext#connect中, 核心方法 findContextOutbound()循環查找隊列中outbound=true的ChannelHandler, 找到後通過invokeConnect進行連接操作, 繼續看invokeConnect()代碼
//AbstractChannelHandlerContext
private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
connect(remoteAddress, localAddress, promise);
}
}
//ChannelOutboundHandlerAdatper
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception {
ctx.connect(remoteAddress, localAddress, promise);
}
如果業務邏輯代碼沒有重寫connect方法, 會調用ChannelOutboundHandlerAdatper的connect()進行連接操作, 而這個方法僅僅調用了ctx.connect(),之後這個調用將會 進入下面調用循環, 注意這裏ChannelHandlerContext 是AbstractChannelHandlerContext
ChannelHandlerContext.connect() -> ChannelHandlerContext.findContextOutbound() -> ChannelHandlerContext.invokeConnect() -> ChannelOutboundHandlerAdatper.connect() -> ChannelHandlerContext.connect()
直到connect事件傳遞到DefaultChannelPipeline雙向隊列的頭節點, handler爲HeadContext, 會直接返回當前對象this, 最終connect鏈接事件將會在head中被處理, 下面是相關邏輯代碼, 到達head後整個全球事件也就結束了
//HeadContext
public ChannelHandler handler() {
return this;
}
//HeadContext
public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
unsafe.connect(remoteAddress, localAddress, promise);
}
6.5 inBound事件傳播方式
從6.1事件傳播流程,大致可以看出inBound和outBound處理事件過程相似, 只是傳播方向不一樣, 在6.2節中提到inbound是事件回調, 響應請求事件
這裏我們接着6.4節繼續分析,connect進行連接之後,肯定需要進行回調操作,而Client連接Server是在AbstractNioChannel中,查看下面代碼
//AbstractNioChannel
public final void connect(
//省略部分其它代碼
try {
boolean wasActive = isActive();
if (doConnect(remoteAddress, localAddress)) { // 執行鏈接操作
fulfillConnectPromise(promise, wasActive); // 鏈接成功,執行事件回調操作
} else {
//省略部分其它代碼
}
} catch (Throwable t) {
promise.tryFailure(annotateConnectException(t, remoteAddress));
closeIfClosed();
}
}
//AbstractNioChannel
private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
// 省略部分其它代碼
if (!wasActive && active) {
pipeline().fireChannelActive();
}
if (!promiseSet) {
close(voidPromise());
}
}
在fulfillConnectPromise()方法中,執行DefaultChannelPipeline的fireChannelActive()方法進行回調操作, 這個應該比較熟悉, 這個方法在前面已經分析過, 以head作爲起始遍歷節點, 這裏我們只關注下面的邏輯
//AbstractChannelHandlerContext
private void invokeChannelActive() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
//ChannelInboundHandlerAdapter
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
}
可以看出之後這裏和outBound調用循環很相似, inbound會進入下面的循環, 注意這裏ChannelHandlerContext 是AbstractChannelHandlerContext
ChannelHandlerContext.fireChannelActive() -> ChannelHandlerContext.findContextInbound -> ChannelHandlerContext.invokeChannelActive() -> ChannelInboundHandlerAdapter.channelActive() -> ChannelHandlerContext.fireChannelActive()
當消息傳遞到TailContext後, 會執行TailContext的channelActive()方法,但是該方法空實現, 默認是不處理。
public void channelActive(ChannelHandlerContext ctx) throws Exception { }
6.6 事件傳播總結
-
Inbound
- Inbound事件是通知型事件, 當某件事件就緒後會自底向上進行通知
- Inbound事件的發起者是unsafe
- inbound事件的處理者是Channel,如果用戶沒有實現自定義處理方法, inbound事件默認的處理者是TailContext(實際是空實現)
- Inbound事件在雙向鏈表中的傳播方向是head -> tail
- ChannelHandler在處理事件時, 如果這個handler不是最後一個handler,會調用ChannelHandlerContext.fireIN_EVT()方法, 將事件傳播下去, 比如上面提到的fireChannelActive(),如果不這麼做事件傳播會終止
-
outBound
- outbound事件是請求型事件
- outbound事件的處理者是unsafe
- outbound事件的發起者是Channel
- outBound事件在雙向鏈表中的傳播方向是 tail -> head
- ChannelHandler在處理事件時, 如果這個handler 不是最後一個handler, 會調用ChannelHandlerContext.OUT_EVT()方法, 將事件傳播下去, 比如上面提到的ctx.connect(), 如果不這麼做事件傳播會終止