前面,我們分析了Netty EventLoop的 建立 與 啟動 原理,接下里我們來分析Netty中另外兩個重要元件—— ChannelHandler 與 Pipeline。Netty中I/O事件的傳播機制均由它負責,下面我們來看看它是如何實現的。
Netty版本:4.1.30
我們前面在講 Channel建立 時,在AbstractChannel的建構函式中, 一筆帶過地提到了Pipeline,現在我們來深入分析一下它的原理。
概述
Netty channel lifecycle
前面,我們在分析 Netty channel 原始碼時,分析了Channel的建立、初始化、註冊、繫結過程。在Netty中,channel的生命週期如下所示:
- ChannelRegistered:Channel註冊到了EventLoop上
- ChannelActive:Channel啟用,連線到了遠端某一個節點上,可以收發資料了
- ChannelInactive:斷開連線
- ChannelUnregistered:Channel從EventLoop上取消註冊
Netty channelHandler
Channel 每一次狀態的變化,都會產生一個事件,呼叫 ChannelHandler 中對應的方法進行處理,我們看下 ChannelHandler的UML,其中最為重要的兩個ChannelHandler:
- ChannelInboundHandler:處理入站資料以及channel的各種狀態變化
- ChannelOutboundHandler:處理出站資料並允許攔截所有操作
Netty ChannelPipeline
前面我們在分析Channel建立過程時,每一個新建立的Channel都將會被分配一個新的ChannelPipeline。ChannelPipeline是一個攔截流經Channel的入站和出站事件的ChannelHandler例項鏈,如圖所示:
一個 Channel 包含了一個 ChannelPipeline,ChannelPipeline內部是一個雙向的連結串列結構,內部由一個個的ChannelHandlerContext節點組成,ChannelPipeline有頭尾兩個固定的節點HeadContext與TailContext。使用者自定的ChannelHandler就是由ChannelHandlerContext包裝成Pipeline的節點,參與Channel整個生命週期中所觸發的入站事件與出站事件以及相應資料流的攔截處理。
根據事件的起源,事件將會被ChannelInboundHandler(入站處理器)或者ChannelOutboundHandler(出站處理器)處理。隨後,通過呼叫ChannelHandlerContext實現,它將被轉發給同一超型別的下一個ChannelHandler,如圖所示:
Pipeline UML
我們先來看下 ChannelPipeline 以及 ChannelHandlerContext 的類圖結構,它們都實現了ChannelInboundInvoker與ChannelOutboundInvoker介面。
Pipeline初始化
AbstractChannel建構函式如下:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
// 建立預設Pipeline
pipeline = newChannelPipeline();
}
// 建立預設Pipeline
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);
// 將tail與head串聯起來
head.next = tail;
tail.prev = head;
}
複製程式碼
我們可以看到Pipeline其實是一個雙向連結串列的結構,剛剛初始化的時候,Pipeline(管道)中只有兩個節點,如圖:
接下來我們看看組成Pipeline節點的物件—— ChannelHandlerContext。
ChannelHandlerContext
ChannelHandlerContext 實現了AttributeMap、ChannelInboundInvoker、ChannelOutboundInvoker介面。Pipeline中的事件傳播,都是由ChannelHandlerContext負責,將發生的事件從一個節點傳到下一個節點。
ChannelHandlerContext介面
public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {
// 返回ChannelHandlerContext中繫結的Channel
Channel channel();
// 返回專用於執行任務的 EventExecutor
EventExecutor executor();
// 返回ChannelHandlerContext的唯一名稱。該名字將在ChannelHandler被新增到ChannelPipeline時會被用到,從ChannelPipeline中訪問註冊的ChannelHandler時,也會被用到。
String name();
// 返回ChannelHandlerContext中繫結的ChannelHandler
ChannelHandler handler();
// 屬於這個ChannelHandlerContext的ChannelHandler從ChannelPipeline移除了,返回true
boolean isRemoved();
@Override
ChannelHandlerContext fireChannelRegistered();
@Override
ChannelHandlerContext fireChannelUnregistered();
@Override
ChannelHandlerContext fireChannelActive();
@Override
ChannelHandlerContext fireChannelInactive();
@Override
ChannelHandlerContext fireExceptionCaught(Throwable cause);
@Override
ChannelHandlerContext fireUserEventTriggered(Object evt);
@Override
ChannelHandlerContext fireChannelRead(Object msg);
@Override
ChannelHandlerContext fireChannelReadComplete();
@Override
ChannelHandlerContext fireChannelWritabilityChanged();
@Override
ChannelHandlerContext read();
@Override
ChannelHandlerContext flush();
// 返回分配的ChannelPipeline
ChannelPipeline pipeline();
// 返回用於分配ByteBuf的ByteBufAllocator
ByteBufAllocator alloc();
}
複製程式碼
AttributeMap介面
實現 AttributeMap 介面,表示ChannelHandlerContext節點可以儲存自定義的屬性。
// 屬性Map介面
public interface AttributeMap {
// 通過Key獲取屬性
<T> Attribute<T> attr(AttributeKey<T> key);
// 判斷屬性是否存在
<T> boolean hasAttr(AttributeKey<T> key);
}
複製程式碼
ChannelInboundInvoker介面
實現ChannelInboundInvoker介面,表示節點可以用於傳播入站相關的事件。
public interface ChannelInboundInvoker {
// 當Channel註冊到EventLoop上時
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的channelRegistered(ChannelHandlerContext)方法
ChannelInboundInvoker fireChannelRegistered();
// 當Channel從EventLoop上取消註冊
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的channelUnregistered(ChannelHandlerContext)方法
ChannelInboundInvoker fireChannelUnregistered();
// 當Channel處理啟用狀態,意味著連線已經建立
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的channelActive(ChannelHandlerContext)方法
ChannelInboundInvoker fireChannelActive();
// 當Channel處理失效狀態,意味著連線已經斷開
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的channelInactive(ChannelHandlerContext)方法
ChannelInboundInvoker fireChannelInactive();
// 在pipeline中某個一個入站(inbound)操作出現了異常
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的exceptionCaught(ChannelHandlerContext)方法
ChannelInboundInvoker fireExceptionCaught(Throwable cause);
// 收到使用者自定義的事件
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的userEventTriggered(ChannelHandlerContext)方法
ChannelInboundInvoker fireUserEventTriggered(Object event);
// Channel接收到了訊息
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的channelRead(ChannelHandlerContext)方法
ChannelInboundInvoker fireChannelRead(Object msg);
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的channelReadComplete(ChannelHandlerContext)方法
ChannelInboundInvoker fireChannelReadComplete();
// 呼叫ChannelPipeline中下一個ChannelInboundHandler的channelWritabilityChanged(ChannelHandlerContext)方法
ChannelInboundInvoker fireChannelWritabilityChanged();
}
複製程式碼
ChannelOutboundInvoker介面
實現ChannelOutboundInvoker介面,意味著節點可以用來處理出站相關的事件。
public interface ChannelOutboundInvoker {
// 將Channel繫結到一個本地地址,這將呼叫ChannelPipeline中的下一個ChannelOutboundHandler的bind(ChannelHandlerContext, Socket- Address, ChannelPromise)方法
ChannelFuture bind(SocketAddress localAddress);
ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);
// 將Channel連線到一個遠端地址,這將呼叫ChannelPipeline中的下一個ChannelOutboundHandler的connect(ChannelHandlerContext, Socket- Address, ChannelPromise)方法
ChannelFuture connect(SocketAddress remoteAddress);
ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress);
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
// 將Channel斷開連線。這將呼叫ChannelPipeline中的下一個ChannelOutbound- Handler的disconnect(ChannelHandlerContext, Channel Promise)方法
ChannelFuture disconnect();
ChannelFuture disconnect(ChannelPromise promise);
// 將Channel關閉。這將呼叫ChannelPipeline中的下一個ChannelOutbound- Handler的close(ChannelHandlerContext, ChannelPromise)方法
ChannelFuture close();
ChannelFuture close(ChannelPromise promise);
// 將Channel從它先前所分配的EventExecutor(即EventLoop)中登出。這將呼叫ChannelPipeline中的下一個ChannelOutboundHandler的deregister (ChannelHandlerContext, ChannelPromise)方法
ChannelFuture deregister();
ChannelFuture deregister(ChannelPromise promise);
// 請求從Channel中讀取更多的資料。這將呼叫ChannelPipeline中的下一個ChannelOutboundHandler的read(ChannelHandlerContext)方法
ChannelOutboundInvoker read();
// 將訊息寫入Channel。這將呼叫ChannelPipeline中的下一個Channel- OutboundHandler的write(ChannelHandlerContext, Object msg, Channel- Promise)方法。注意:這並不會將訊息寫入底層的Socket,而只會將它放入佇列中。要將它寫入Socket,需要呼叫flush()或者writeAndFlush()方法
ChannelFuture write(Object msg);
ChannelFuture write(Object msg, ChannelPromise promise);
// 沖刷Channel所有掛起的寫入。這將呼叫ChannelPipeline中的下一個Channel- OutboundHandler的flush(ChannelHandlerContext)方法
ChannelOutboundInvoker flush();
// 這是一個先呼叫write()方法再接著呼叫flush()方法的便利方法
ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);
ChannelFuture writeAndFlush(Object msg);
ChannelPromise newPromise();
ChannelProgressivePromise newProgressivePromise();
ChannelFuture newSucceededFuture();
ChannelFuture newFailedFuture(Throwable cause);
ChannelPromise voidPromise();
}
複製程式碼
TailContext & HeadContext
接下來,我們看看Pipeline中的頭部與尾部節點。
TailContext節點
TailContext是尾部節點,inbound型別,主要處理Pipeline中資料流的收尾工作。
final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
TailContext(DefaultChannelPipeline pipeline) {
// 呼叫AbstractChannelHandlerContext構造器
// TailContext是一個inbound(入站)節點
super(pipeline, null, TAIL_NAME, true, false);
// 設定新增完成
setAddComplete();
}
// 返回Handler,就是它自身
@Override
public ChannelHandler handler() {
return this;
}
...
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
onUnhandledInboundException(cause);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
onUnhandledInboundMessage(msg);
}
...
}
// 如果pipeline中有異常沒做處理,最終會由TailContext打贏一個警告日誌
protected void onUnhandledInboundException(Throwable cause) {
try {
logger.warn(
"An exceptionCaught() event was fired, and it reached at the tail of the pipeline. " +
"It usually means the last handler in the pipeline did not handle the exception.",
cause);
} finally {
// 釋放物件
ReferenceCountUtil.release(cause);
}
}
// 如果pipeline中有read訊息沒有處理,最終會由TailContext打贏一個警告日誌
protected void onUnhandledInboundMessage(Object msg) {
try {
logger.debug(
"Discarded inbound message {} that reached at the tail of the pipeline. " +
"Please check your pipeline configuration.", msg);
} finally {
ReferenceCountUtil.release(msg);
}
}
// 設定 ChannelHandlerContext 狀態為新增完成,狀態=2
final void setAddComplete() {
for (;;) {
int oldState = handlerState;
if (oldState == REMOVE_COMPLETE || HANDLER_STATE_UPDATER.compareAndSet(this, oldState, ADD_COMPLETE)) {
return;
}
}
}
複製程式碼
AbstractChannelHandlerContext
AbstractChannelHandlerContext 是 ChannelHandlerContext 的抽象實現:
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
implements ChannelHandlerContext, ResourceLeakHint {
...
// 下一個節點
volatile AbstractChannelHandlerContext next;
// 上一個節點
volatile AbstractChannelHandlerContext prev;
// 是否為inBound型別
private final boolean inbound;
// 是否為outbound型別
private final boolean outbound;
// 繫結的預設pipeline
private final DefaultChannelPipeline pipeline;
// 節點名
private final String name;
private final boolean ordered;
// Will be set to null if no child executor should be used, otherwise it will be set to the
// child executor.
final EventExecutor executor;
...
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,boolean inbound, boolean outbound) {
// 設定HandlerContext名稱
this.name = ObjectUtil.checkNotNull(name, "name");
// 繫結pipeline
this.pipeline = pipeline;
// 繫結executor(這裡為null)
this.executor = executor;
// 如果節點為inbound型別就設定為true
this.inbound = inbound;
// 如果節點為outbound型別就設定為true
this.outbound = outbound;
// Its ordered if its driven by the EventLoop or the given Executor is an instanceof OrderedEventExecutor.
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
...
}
複製程式碼
DefaultChannelHandlerContext
final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext {
private final ChannelHandler handler;
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
// 呼叫 AbstractChannelHandlerContext 建構函式
super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
}
@Override
public ChannelHandler handler() {
return handler;
}
// 是否為inBound型別
private static boolean isInbound(ChannelHandler handler) {
return handler instanceof ChannelInboundHandler;
}
// 是否為outBound型別
private static boolean isOutbound(ChannelHandler handler) {
return handler instanceof ChannelOutboundHandler;
}
}
複製程式碼
HeadContext
HeadContext是頭部節點,outbound型別,用於傳播事件和進行一些底層socket操作。
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
private final Unsafe unsafe;
HeadContext(DefaultChannelPipeline pipeline) {
// 呼叫AbstractChannelHandlerContext構造器
// HeadContext是一個outbound(出站)節點
super(pipeline, null, HEAD_NAME, false, true);
// 設定Unsafe物件
unsafe = pipeline.channel().unsafe();
// 設定新增完成
setAddComplete();
}
// 返回ChannelHandler,就只它自身
@Override
public ChannelHandler handler() {
return this;
}
@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
// 呼叫 unsafe 進行bind操作
unsafe.bind(localAddress, promise);
}
@Override
public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
// 呼叫 unsafe 進行 connect 操作
unsafe.connect(remoteAddress, localAddress, promise);
}
@Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
// 呼叫 unsafe 進行 disconnect 操作
unsafe.disconnect(promise);
}
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
// 呼叫 unsafe 進行 close 操作
unsafe.close(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
// 呼叫 unsafe 進行 deregister 操作
unsafe.deregister(promise);
}
@Override
public void read(ChannelHandlerContext ctx) {
// 呼叫 unsafe 進行 read 操作
unsafe.beginRead();
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// 呼叫 unsafe 進行 write 操作
unsafe.write(msg, promise);
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
// 呼叫 unsafe 進行 flush 操作
unsafe.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 傳播ExceptionCaught事件
ctx.fireExceptionCaught(cause);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
invokeHandlerAddedIfNeeded();
// 傳播channelRegistered事件
ctx.fireChannelRegistered();
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
// 傳播channelUnregistered事件
ctx.fireChannelUnregistered();
// Remove all handlers sequentially if channel is closed and unregistered.
if (!channel.isOpen()) {
destroy();
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 傳播 channelActive 事件
ctx.fireChannelActive();
// 在 https://wangwei.one/posts/netty-channel-source-analyse.html 中分析過了
// 主要是在channel啟用之後,向底層的selector註冊一個SelectionKey.OP_ACCEPT監聽事件
// 這樣channel在連線之後,就可以監聽到一個read事件
readIfIsAutoRead();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 傳播 channelInactive 事件
ctx.fireChannelInactive();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 傳播 channelRead 事件
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// 傳播 channelReadComplete 事件
ctx.fireChannelReadComplete();
//
readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
// 傳播 userEventTriggered 事件
ctx.fireUserEventTriggered(evt);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
// 傳播 channelWritabilityChanged 事件
ctx.fireChannelWritabilityChanged();
}
}
複製程式碼
Pipeline 節點新增
上面我們分析了Pipeline的基本結構,接下來我們看看Pipeline新增節點(也就是Handler處理器)的過程。該過程主要分為三步:
- 判斷是否重複新增
- 建立節點並新增至連結串列
- 回撥新增完成事件
以這段常見的程式碼為例:
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
// 新增 serverHandler
ch.pipeline().addLast(serverHandler);
}
});
ChannelFuture f = b.bind().sync();
複製程式碼
我們從 ChannelPipeline.addLast() 方法進去:
public class DefaultChannelPipeline implements ChannelPipeline {
...
@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
@Override
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
if (handlers == null) {
throw new NullPointerException("handlers");
}
// 迴圈處理
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
}
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 檢查是否重複
checkMultiplicity(handler);
// 建立新節點
newCtx = newContext(group, filterName(name, handler), handler);
// 新增新節點
addLast0(newCtx);
// 如果 registered 為 false,則表示這個channel還未註冊到EventLoop上.
// 在這種情況下,我們新增一個Task到PendingHandlerCallback中,
// 等到這個channel註冊成功之後,將會呼叫立即呼叫 ChannelHandler.handlerAdded(...) 方法,已達到channel新增的目的
if (!registered) {
// 設定為待新增狀態
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
// 獲取executor
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
// 設定為待新增狀態
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
// 回撥新增完成事件
callHandlerAdded0(newCtx);
}
});
return this;
}
}
// 回撥新增完成事件
callHandlerAdded0(newCtx);
return this;
}
// 檢查是否重複
private static void checkMultiplicity(ChannelHandler handler) {
// handler是否為ChannelHandlerAdapter型別,不是則不做處理
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
// 判斷handler是否新增了Sharable註解 && 是否新增過了
if (!h.isSharable() && h.added) {
throw new ChannelPipelineException(
h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times.");
}
h.added = true;
}
}
// 建立新的節點
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
// 呼叫DefaultChannelHandlerContext的建構函式
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
// 在tail節點之前新增新節點
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
// 回撥ChannelHandler中的handlerAdded方法
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
try {
// 我們必須在handlerAdded方法之前呼叫setAddComplete方法。否則的話,一旦handlerAdded方法產生了任何pipeline事件,由於狀態的緣故,ctx.handler()將會丟失這些事件的處理。
// 設定新節點的狀態為新增完成狀態
ctx.setAddComplete();
// 呼叫handlerAdded介面
ctx.handler().handlerAdded(ctx);
} catch (Throwable t) {
...
// 如果新增失敗,則刪除新節點
remove0(ctx);
...
}
}
...
}
複製程式碼
我們來看下setAddComplete()方法:
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
implements ChannelHandlerContext, ResourceLeakHint {
...
// 通過自旋操作,設定狀態為ADD_COMPLETE
final void setAddComplete() {
for (;;) {
int oldState = handlerState;
// Ensure we never update when the handlerState is REMOVE_COMPLETE already.
// oldState is usually ADD_PENDING but can also be REMOVE_COMPLETE when an EventExecutor is used that is not
// exposing ordering guarantees.
if (oldState == REMOVE_COMPLETE || HANDLER_STATE_UPDATER.compareAndSet(this, oldState, ADD_COMPLETE)) {
return;
}
}
}
...
// 設定為 ADD_PENDING 狀態
final void setAddPending() {
boolean updated = HANDLER_STATE_UPDATER.compareAndSet(this, INIT, ADD_PENDING);
assert updated;
// This should always be true as it MUST be called before setAddComplete() or setRemoved().
}
...
}
複製程式碼
回撥使用者自定義Handler中的handlerAdded方法:
@Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter {
...
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.printf("ServerHandler added ....");
}
...
}
複製程式碼
ChannelInitializer
關於回撥ChannelHandler中的handlerAdded()方法,最常見的一個場景就是,使用 ChannelInitializer 來新增我們自定義的ChannelHandler。ChannelInitializer被新增完成之後,會回撥到它的 initChannel 方法。
接下來,我們看看 ChannelInitializer 這個類,它是一個特殊的ChannelInboundHandler,它提供了一種在Channel註冊到EventLoop後初始化Channel的簡便方法。
@Sharable
public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {
private final ConcurrentMap<ChannelHandlerContext, Boolean> initMap = PlatformDependent.newConcurrentHashMap();
/**
* 當 ch 註冊成功之後,該方法就會被呼叫,該方法結束返回之後,此ChannelInitializer例項將會從Channel所繫結的ChannelPipeline中移除
*
* @param ch 所註冊的Channel
*
*/
protected abstract void initChannel(C ch) throws Exception;
...
// ChannelInitializer 新增成功後,會回撥到handlerAdded()介面
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// This should always be true with our current DefaultChannelPipeline implementation.
// The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
// surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
// will be added in the expected order.
initChannel(ctx);
}
}
@SuppressWarnings("unchecked")
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
// 標記ctx為true,且之前沒有標記過。防止重複執行
if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) {
try {
// 呼叫initChannel方法
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 {
// 最終會刪除 ChannelInitializer 例項
remove(ctx);
}
return true;
}
return false;
}
// 刪除 ChannelInitializer 例項
private void remove(ChannelHandlerContext ctx) {
try {
// 獲取 Pipeline
ChannelPipeline pipeline = ctx.pipeline();
// 從 Pipeline 中返回 ChannelInitializer 例項
if (pipeline.context(this) != null) {
// 刪除 ChannelInitializer 例項
// 刪除邏輯請看下一小節
pipeline.remove(this);
}
} finally {
initMap.remove(ctx);
}
}
}
複製程式碼
遍歷 ChannelHandlerContext 節點查詢出ChannelHandler例項
public class DefaultChannelPipeline implements ChannelPipeline {
...
// 通過handler獲取ChannelHandlerContext
@Override
public final ChannelHandlerContext context(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
AbstractChannelHandlerContext ctx = head.next;
for (;;) {
if (ctx == null) {
return null;
}
if (ctx.handler() == handler) {
return ctx;
}
ctx = ctx.next;
}
}
...
}
複製程式碼
Pipeline中除了addLast方法外, 還有addFirst、addBefore、addAfter等方法,邏輯類似,可以自行研究學習。
Pipeline 節點刪除
上面,我們講了Pipeline節點的新增,這小結我們看看Pipeline節點的刪除功能。
netty 有個最大的特性之一就是Handler可插拔,做到動態編織pipeline,比如在首次建立連線的時候,需要通過進行許可權認證,在認證通過之後,就可以將此context移除,下次pipeline在傳播事件的時候就就不會呼叫到許可權認證處理器。
下面是許可權認證Handler最簡單的實現,第一個資料包傳來的是認證資訊,如果校驗通過,就刪除此Handler,否則,直接關閉連線
// 鑑權Handler
public class AuthHandler extends SimpleChannelInboundHandler<ByteBuf> {
...
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf data) throws Exception {
if (verify(authDataPacket)) {
ctx.pipeline().remove(this);
} else {
ctx.close();
}
}
private boolean verify(ByteBuf byteBuf) {
//...
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("AuthHandler has been removed ! ");
}
}
複製程式碼
我們來看看 DefaultChannelPipeline 中的 remove 方法:
public class DefaultChannelPipeline implements ChannelPipeline {
...
// 從Pipeline中刪除ChannelHandler
@Override
public final ChannelPipeline remove(ChannelHandler handler) {
remove(getContextOrDie(handler));
return this;
}
...
// 獲取 ChannelHandler ,獲取不到就丟擲異常
private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
if (ctx == null) {
throw new NoSuchElementException(handler.getClass().getName());
} else {
return ctx;
}
}
...
// 刪除
private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
// ctx不能為heand與tail
assert ctx != head && ctx != tail;
synchronized (this) {
// 從pipeline中刪除ChannelHandlerContext節點
remove0(ctx);
// 如果為false,則表明channel還沒有註冊到eventloop上
// 在刪除這種場景下,我們先新增一個Task,一旦channel註冊成功就會呼叫這個Task,這個Task就會立即呼叫ChannelHandler.handlerRemoved(...)方法,來從pipeline中刪除context。
if (!registered) {
callHandlerCallbackLater(ctx, false);
return ctx;
}
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@Override
public void run() {
// 回撥 handlerRemoved 方法
callHandlerRemoved0(ctx);
}
});
return ctx;
}
}
// 回撥 handlerRemoved 方法
callHandlerRemoved0(ctx);
return ctx;
}
...
// 刪除節點 ChannelHandlerContext
private static void remove0(AbstractChannelHandlerContext ctx) {
AbstractChannelHandlerContext prev = ctx.prev;
AbstractChannelHandlerContext next = ctx.next;
prev.next = next;
next.prev = prev;
}
...
private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) {
// Notify the complete removal.
try {
try {
// 回撥 handlerRemoved 方法
// 也就是我們前面例子 AuthHandler 中的 handlerRemoved() 方法
ctx.handler().handlerRemoved(ctx);
} finally {
// 設定為ctx 狀態為 REMOVE_COMPLETE
ctx.setRemoved();
}
} catch (Throwable t) {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));
}
}
...
}
複製程式碼
好了, 刪除的邏輯就分析到這裡了。
小結
這一講我們分析了Pipeline的建立過程,瞭解Pipeline中的連結串列結構以及每個節點的資料結構。還分析了Pipeline是如何新增節點的,又是如何刪除節點的。接下來 ,我們會分析Pipeline如何進行事件傳播的。