共計 3181 個字符,預(yù)計需要花費 8 分鐘才能閱讀完成。
這篇文章主要介紹“netty handler 的執(zhí)行順序是什么”,在日常操作中,相信很多人在 netty handler 的執(zhí)行順序是什么問題上存在疑惑,丸趣 TV 小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”netty handler 的執(zhí)行順序是什么”的疑惑有所幫助!接下來,請跟著丸趣 TV 小編一起來學(xué)習(xí)吧!
handler 的概念,讓我想到了其他的一些東西,就好像 servlet 當(dāng)中的 filter,spring 當(dāng)中的 interceptor。在 handler 當(dāng)中,能夠完成各種各樣的工作,協(xié)議流的編解碼、特殊信息的攔截、請求數(shù)量的統(tǒng)計等等,或者可以這樣說,所有的業(yè)務(wù)層面的東西,都需要在 handler 當(dāng)中來完成。
我會按照上行和下行兩類來分析 handler 的執(zhí)行順序,今天先來下行的。
按照我最初的想象,所有的 handler 應(yīng)該都是同一類東西,那么我們業(yè)務(wù)執(zhí)行的方法,也就是我們繼承 netty 提供的父類之后,Override 的方法,應(yīng)該是同一個,可是我實際使用當(dāng)中發(fā)現(xiàn)不是這么回事,有時候是一個 decode 方法,有時候是一個 messageReceived 方法,這是什么道理。基于這個困惑,才會有了今天的這篇文章。
首先是一個 stacktrace 的圖。
ProtocolAnaDecoder 是我自己寫的一個協(xié)議解析類,繼承自 ByteToMessageDecoder,在這個類里面依次調(diào)用了 3 個方法,channelRead(),callDecode(),decode()。這其中 decode,是我們自己實現(xiàn)的,其他 2 個方法來自父類。
再看另一個圖。
NettyServerHandler 繼承自 SimpleChannelInboundHandler,這里依次調(diào)用了 channelRead(),messageReceived() 這樣 2 個方法。
到此為止基本就解決了我的第一個疑惑,最初都來自 channelRead。那么這個 channelRead 雖然在各種 handler 當(dāng)中都有實現(xiàn),但是它的最初的定義來自 ChannelHandler,這是一個 interface。而它的實現(xiàn) ChannelHandlerAdapter,基本可以看做 netty 當(dāng)中所有 handler 的老祖宗。(這里之所以要說基本,是因為有 2 個 web 相關(guān)的 handler interface,直接繼承了 ChannelHandler,但這個不是我們今天討論的重點)
繼續(xù),就該是 ChannelHandlerInvokerUtil.invokeChannelReadNow,看代碼吧。
public static void invokeChannelReadNow(final ChannelHandlerContext ctx, final Object msg) {
try {
ctx.handler().channelRead(ctx, msg);
} catch (Throwable t) {
notifyHandlerException(ctx, t);
}
}
清楚明白,很好理解。
然后是 DefaultChannelHandlerInvoker.invokeChannelRead,代碼如下:
@Override
public void invokeChannelRead(final ChannelHandlerContext ctx, final Object msg) {
if (msg == null) {
throw new NullPointerException(msg
}
if (executor.inEventLoop()) {
invokeChannelReadNow(ctx, msg);
} else {
safeExecuteInbound(new Runnable() {
@Override
public void run() {
invokeChannelReadNow(ctx, msg);
}
}, msg);
}
}
executor.inEventLoop(),當(dāng)前 channel 的 executor 是否處于時間循環(huán)當(dāng)中,好吧,到目前為止,我也不知道什么時候會走到 else 里面去,這里只好留待以后再去搞搞清楚了。
再往前走,DefaultChannelHandlerContext.fireChannelRead,代碼如下:
public ChannelHandlerContext fireChannelRead(Object msg) {
DefaultChannelHandlerContext next = findContextInbound(MASK_CHANNEL_READ);
next.invoker.invokeChannelRead(next, msg);
return this;
}
handler 的依次執(zhí)行就在這里面體現(xiàn)了。
繼續(xù),DefaultChannelPipeline.fireChannelRead,代碼如下:
public ChannelPipeline fireChannelRead(Object msg) {
head.fireChannelRead(msg);
return this;
}
好了,如果沒記錯的話,我們最初聲明一個 netty 的時候,就是把一系列的 handler 加到了 channel pipeline 當(dāng)中。那么這一系列的 handler 在 pipeline 當(dāng)中是如何保存的呢。我首先先看一下 DefaultChannelPipeline 的構(gòu)造函數(shù):
public DefaultChannelPipeline(AbstractChannel channel) {
if (channel == null) {
throw new NullPointerException(channel
}
this.channel = channel;
TailHandler tailHandler = new TailHandler();
tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);
HeadHandler headHandler = new HeadHandler(channel.unsafe());
head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
head.next = tail;
tail.prev = head;
}
首先生命了一個 tail 和一個 head,然后把這 2 個對象構(gòu)成了一個雙向鏈表。
再看一下 addlast 方法:
private void addLast0(final String name, DefaultChannelHandlerContext newCtx) {
checkMultiplicity(newCtx);
DefaultChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
name2ctx.put(name, newCtx);
callHandlerAdded(newCtx);
}
很清楚,在鏈表當(dāng)中插入一個元素。再對照一下前面的代碼,首先從 head 開始,但它并不完成實際工作,直接取它的 next 來執(zhí)行,之后依次便利鏈表。
到此,關(guān)于“netty handler 的執(zhí)行順序是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注丸趣 TV 網(wǎng)站,丸趣 TV 小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>