netty handler的执行顺序是什么

81次阅读
没有评论

共计 3181 个字符,预计需要花费 8 分钟才能阅读完成。

这篇文章主要介绍“netty handler 的执行顺序是什么”,在日常操作中,相信很多人在 netty handler 的执行顺序是什么问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”netty handler 的执行顺序是什么”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!

handler 的概念,让我想到了其他的一些东西,就好像 servlet 当中的 filter,spring 当中的 interceptor。在 handler 当中,能够完成各种各样的工作,协议流的编解码、特殊信息的拦截、请求数量的统计等等,或者可以这样说,所有的业务层面的东西,都需要在 handler 当中来完成。

我会按照上行和下行两类来分析 handler 的执行顺序,今天先来下行的。

按照我最初的想象,所有的 handler 应该都是同一类东西,那么我们业务执行的方法,也就是我们继承 netty 提供的父类之后,Override 的方法,应该是同一个,可是我实际使用当中发现不是这么回事,有时候是一个 decode 方法,有时候是一个 messageReceived 方法,这是什么道理。基于这个困惑,才会有了今天的这篇文章。

首先是一个 stacktrace 的图。

ProtocolAnaDecoder 是我自己写的一个协议解析类,继承自 ByteToMessageDecoder,在这个类里面依次调用了 3 个方法,channelRead(),callDecode(),decode()。这其中 decode,是我们自己实现的,其他 2 个方法来自父类。

再看另一个图。

NettyServerHandler 继承自 SimpleChannelInboundHandler,这里依次调用了 channelRead(),messageReceived() 这样 2 个方法。

到此为止基本就解决了我的第一个疑惑,最初都来自 channelRead。那么这个 channelRead 虽然在各种 handler 当中都有实现,但是它的最初的定义来自 ChannelHandler,这是一个 interface。而它的实现 ChannelHandlerAdapter,基本可以看做 netty 当中所有 handler 的老祖宗。(这里之所以要说基本,是因为有 2 个 web 相关的 handler interface,直接继承了 ChannelHandler,但这个不是我们今天讨论的重点)

继续,就该是 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(),当前 channel 的 executor 是否处于时间循环当中,好吧,到目前为止,我也不知道什么时候会走到 else 里面去,这里只好留待以后再去搞搞清楚了。

再往前走,DefaultChannelHandlerContext.fireChannelRead,代码如下:

public ChannelHandlerContext fireChannelRead(Object msg) {
  DefaultChannelHandlerContext next = findContextInbound(MASK_CHANNEL_READ);
  next.invoker.invokeChannelRead(next, msg);
  return this;
  }

handler 的依次执行就在这里面体现了。

继续,DefaultChannelPipeline.fireChannelRead,代码如下:

public ChannelPipeline fireChannelRead(Object msg) {
  head.fireChannelRead(msg);
  return this;
  }

好了,如果没记错的话,我们最初声明一个 netty 的时候,就是把一系列的 handler 加到了 channel pipeline 当中。那么这一系列的 handler 在 pipeline 当中是如何保存的呢。我首先先看一下 DefaultChannelPipeline 的构造函数:

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 个对象构成了一个双向链表。

再看一下 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);
  }

很清楚,在链表当中插入一个元素。再对照一下前面的代码,首先从 head 开始,但它并不完成实际工作,直接取它的 next 来执行,之后依次便利链表。

到此,关于“netty handler 的执行顺序是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-16发表,共计3181字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)