Netty 权威指南笔记(七):ChannelPipeline 和 ChannelHandler 源码分析
来源:互联网 发布:凯文凯利 人工智能 编辑:程序博客网 时间:2024/05/22 07:45
- Netty 权威指南笔记七ChannelPipeline 和 ChannelHandler 源码分析
- 概述
- ChannelPipeline 源码分析
- ChannelHandler 双向链表
- 事件处理流程
- ChannelHandler 源码分析
- 类图
- ChannelHandler
- ChannelHandlerAdapter
- ByteToMessageDecoder
- FixedLengthFrameDecoder
Netty 权威指南笔记(七):ChannelPipeline 和 ChannelHandler 源码分析
文中源码版本为 Netty4.1。
概述
Netty 的 ChannelPipeline 和 ChannelHandler 机制类似于 Servlet 和 Filter 过滤器,这类拦截器实际上是职责链模式的一种变形,主要是为了方便事件的拦截和用户业务逻辑的定制。
Servlet Filter 过滤器提供了一种面向对象的模块化机制,用来将公共人物封装到可插入的组件中。这些组件通过 Web 部署配置文件(web.xml)进行声明,无须改动代码即可添加和删除过滤器。可以对应于程序 Servlet 提供的核心功能进行补充,而不破坏原有的功能。
Netty 的 Channel 过滤器实现原理与 Servlet Filter 机制一致,它将 Channel 的数据管道抽象为 ChannelPipeline,消息在 ChannelPipeline 中流动和传递。ChannelPipeline 持有 I/O 事件拦截器 ChannelHandler 的链表,由 ChannelHandler 来对 I/O 事件进行具体的拦截和处理,可以方便地通过新增和删除 ChannelHandler 来实现不同业务逻辑的定制,能够实现对修改封闭和对扩展到支持。
ChannelPipeline 源码分析
ChannelHandler 双向链表
在 TimeServer 程序 中,调用了 ChannelPipeline 的 addLast 方法来添加 ChannelHandler。那么 ChannelHandler 在其中是如何存储的呢?
ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 2014) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(16)); socketChannel.pipeline().addLast(new TimeServerHandler()); } });
我们看一下 ChannelPipeline 的成员变量,前两个就是 ChannelHandler 链表的首尾引用,其类型是 AbstractChannelHandlerContext,该类主要包含一个双向链表节点的前置和后置节点引用 prev、next,以及数据引用 handler,相当于链表数据结构中的 Node 节点。
// ChannelHandler 首位指针 final AbstractChannelHandlerContext head; final AbstractChannelHandlerContext tail; // pipeline 所属 channel private final Channel channel; private final ChannelFuture succeededFuture; private final VoidChannelPromise voidPromise; private final boolean touch = ResourceLeakDetector.isEnabled(); 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; }
AbstractChannelHandlerContext 类主要成员变量:
// in AbstractChannelHandlerContext 抽象类 volatile AbstractChannelHandlerContext next; volatile AbstractChannelHandlerContext prev; // DefaultChannelHandlerContext 实现类 private final ChannelHandler handler;
TimeServer 程序中调用的 addLast 方法源码如下:
1. 首先进行了能否被共享的检查。
2. 然后构建了 AbstractChannelHandlerContext 节点,并加入到了链表尾部。
3. 如果 channel 尚未注册到 EventLoop,就添加一个任务到 PendingHandlerCallback 上,后续注册完毕,再调用 ChannelHandler.handlerAdded。
4. 如果已经注册,马上调用 callHandlerAdded0 方法来执行 ChannelHandler.handlerAdded。
@Override public final ChannelPipeline addLast(String name, ChannelHandler handler) { return addLast(null, name, handler); } @Override public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { // 检查,若不是 Sharable,而且已经被添加到其他 pipeline,则抛出异常 checkMultiplicity(handler); // 构建 AbstractChannelHandlerContext 节点 newCtx = newContext(group, filterName(name, handler), handler); // 添加到链表尾部 addLast0(newCtx); // registered 为 false 表示 channel 尚未注册到 EventLoop 上。 // 添加一个任务到 PendingHandlerCallback 上,后续注册完毕,再调用 ChannelHandler.handlerAdded if (!registered) { newCtx.setAddPending(); callHandlerCallbackLater(newCtx, true); return this; } // registered 为 true,则立即调用 ChannelHandler.handlerAdded EventExecutor executor = newCtx.executor(); // inEvent 用于判断当前线程是否是 EventLoop 线程。执行 ChannelHandler 时,必须在对应的 EventLoop 线程池中执行。 if (!executor.inEventLoop()) { newCtx.setAddPending(); executor.execute(new Runnable() { @Override public void run() { callHandlerAdded0(newCtx); } }); return this; } } callHandlerAdded0(newCtx); return this; } // 使用 AbstractChannelHandlerContext 包裹 ChannelHandler private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); } // 将新节点插入链表尾部 private void addLast0(AbstractChannelHandlerContext newCtx) { AbstractChannelHandlerContext prev = tail.prev; newCtx.prev = prev; newCtx.next = tail; prev.next = newCtx; tail.prev = newCtx; }
事件处理流程
Netty 中的事件分为 inbound 事件和 outbound 事件。inbound 事件通常由 I/O 线程触发,比如:
1. 注册事件 fireChannelRegistered。
2. 连接建立事件 fireChannelActive。
3. 读事件和读完成事件 fireChannelRead、fireChannelReadComplete。
4. 异常通知事件 fireExceptionCaught。
5. 用户自定义事件 fireUserEventTriggered。
6. Channel 可写状态变化事件 fireChannelWritabilityChanged。
7. 连接关闭事件 fireChannelInactive。
outbound 事件通常是由用户主动出发的 I/O 事件,比如:
1. 端口绑定 bind。
2. 连接服务端 connect。
3. 写事件 write。
4. 刷新时间 flush。
5. 读事件 read。
6. 主动断开连接 disconnect。
7. 关闭 channel 事件 close。
public final ChannelPipeline fireChannelActive() { AbstractChannelHandlerContext.invokeChannelActive(head); return this; } @Override public final ChannelPipeline fireChannelRead(Object msg) { AbstractChannelHandlerContext.invokeChannelRead(head, msg); return this; } @Override public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { return tail.bind(localAddress, promise); } @Override public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { return tail.connect(remoteAddress, promise); }
看代码我们发现,inbound 事件是从 HeadContext 开始处理的,而 outbound 事件都是由 TailContext 首先处理的。其中的原因是,HeadContext 负责与 NIO 底层的 SocketChannel、ServerSocketChannel 进行交互(通过 Unsafe 类)。
final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler { private final Unsafe unsafe; HeadContext(DefaultChannelPipeline pipeline) { super(pipeline, null, HEAD_NAME, false, true); unsafe = pipeline.channel().unsafe(); setAddComplete(); } @Override public void bind( ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.bind(localAddress, promise); } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { unsafe.write(msg, promise); } }
下面我们以读事件 fireChannelRead 为例看一下其处理流程,在 DefaultChannelPipeline 中调用了 AbstractChannelHandlerContext 类的 invokeChannelRead 方法,其源码如下:
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) { // 如果 msg 实现了 ReferenceCounted 接口,进行处理。 final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next); EventExecutor executor = next.executor(); // 调用 invokeChannelRead 方法 if (executor.inEventLoop()) { next.invokeChannelRead(m); } else { executor.execute(new Runnable() { @Override public void run() { next.invokeChannelRead(m); } }); } } private void invokeChannelRead(Object msg) { // 先调用 channelRead 方法,再 fireChannelRead 触发下一个节点的 channelRead 方法 if (invokeHandler()) { try { ((ChannelInboundHandler) handler()).channelRead(this, msg); } catch (Throwable t) { notifyHandlerException(t); } } else { fireChannelRead(msg); } } @Override public ChannelHandlerContext fireChannelRead(final Object msg) { invokeChannelRead(findContextInbound(), msg); return this; } // inbound 事件中的下一个节点是本节点 next 引用所指节点,而 outbound 事件相反。 private AbstractChannelHandlerContext findContextInbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.next; } while (!ctx.inbound); return ctx; }
ChannelHandler 源码分析
ChannelHandler 类似于 Servlet 的 Filter 过滤器,负责对 I/O 事件进行拦截和处理,可以选择性地处理,也可以透传和终止事件的传递。基于 ChannelHandler 接口,用户可以方便地进行业务逻辑定制,比如打印日志,统一封装异常信息等。
类图
ChannelHandler 类图如下所示:
前面提到 Netty 事件分为 inbound 和 outbound 两类,分别对应 ChannelInboundHandler 和 ChannelOutboundHandler,它们的公共父类就是 ChannelHandler。
ChannelHandler、ChannelInboundHandler 和 ChannelOutboundHandler 接口中,提供了许多方法。在实际使用中,用户往往只需要其中的一两个。为了方便用户使用,有几个抽象类(ChannelHandlerAdapter、ChannelInboundHandlerAdapter、ChannelOutboundHandlerAdapter)提供了一些默认实现,如此用户只需要实现自己关心的方法便可。
类图倒数第二层提供了一些编解码器的抽象类,用户可以在此基础上进行扩展。最后一层是几种常见的编解码器。
下面我们选择几个典型类来解读其源码。
ChannelHandler
ChannelHandler 只有少数几个方法,用于处理 ChannelHandler 被添加时做一些初始化操作,被移除时做一些销毁操作,以及异常处理。
除此之外,还有一个注解 Sharable,用于标识一个 ChannelHandler 实例可以被多个 ChannelPipeline 共享。
public interface ChannelHandler { void handlerAdded(ChannelHandlerContext ctx) throws Exception; void handlerRemoved(ChannelHandlerContext ctx) throws Exception; @Deprecated void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception; @Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Sharable { // no value }}
ChannelInboundHandler 和 ChannelOutboundHandler 接口中的方法和 “事件处理流程” 那一节中介绍的 inbound 和 outbound 事件类型基本上可以一一对应,这里就不贴代码分析了。
ChannelHandlerAdapter
ChannelHandlerAdapter 抽象类,提供了 ChannelHandler 接口方法的默认实现,以及根据注解判断该类是否可共享的 isSharable 方法。
handlerAdded 和 handlerRemoved 的默认实现都是空。
public abstract class ChannelHandlerAdapter implements ChannelHandler { /** * Return {@code true} if the implementation is {@link Sharable} and so can be added * to different {@link ChannelPipeline}s. */ public boolean isSharable() { /** * Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a * {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different * {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of * {@link Thread}s are quite limited anyway. * * See <a href="https://github.com/netty/netty/issues/2289">#2289</a>. */ Class<?> clazz = getClass(); Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache(); Boolean sharable = cache.get(clazz); if (sharable == null) { sharable = clazz.isAnnotationPresent(Sharable.class); cache.put(clazz, sharable); } return sharable; } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // NOOP } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // NOOP }}
ChannelInboundHandlerAdapter、ChannelOutboundHandlerAdapter 分别提供了 ChannelInboundHandler 和 ChannelOutboundHandler 的默认实现。以 ChannelInboundHandlerAdapter 为例,其大多数方法的默认实现都是调用 ChannelHandlerContext 的类似方法,作用为向后传递。
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler { @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelRegistered(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelActive(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ctx.fireChannelRead(msg); }}
ByteToMessageDecoder
该方法提供了将 ByteBuf 转化为对象的解码器处理流程,具体的解码规则交由子类去实现。
我们以读操作 channelRead 为例来研究一下:
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof ByteBuf) { // out 是一个链表,存放解码成功的对象 CodecOutputList out = CodecOutputList.newInstance(); try { ByteBuf data = (ByteBuf) msg; // cumulation 中存放的是上次未处理完的半包消息 first = cumulation == null; if (first) { cumulation = data; } else { // 本次处理,需要把上次遗留的半包和本次数据拼接后,一起处理 cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data); } // 调用解码器解码消息 callDecode(ctx, cumulation, out); } catch (DecoderException e) { throw e; } catch (Exception e) { throw new DecoderException(e); } finally { if (cumulation != null && !cumulation.isReadable()) { numReads = 0; cumulation.release(); cumulation = null; } else if (++ numReads >= discardAfterReads) { numReads = 0; discardSomeReadBytes(); } int size = out.size(); decodeWasNull = !out.insertSinceRecycled(); // 如果有解码成功的数据,需要向后传递,让其他 ChannelHandler 继续处理 fireChannelRead(ctx, out, size); out.recycle(); } } else { ctx.fireChannelRead(msg); } } protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { try { while (in.isReadable()) { int outSize = out.size(); if (outSize > 0) { // 如果有解码成功的数据,需要向后传递,让其他 ChannelHandler 继续处理 fireChannelRead(ctx, out, outSize); out.clear(); // 如果当前 ChannelHandler 所属 ctx 被剔除 pipeline 上下文,就不需要继续处理了 if (ctx.isRemoved()) { break; } outSize = 0; } int oldInputLength = in.readableBytes(); // 解码 decodeRemovalReentryProtection(ctx, in, out); if (ctx.isRemoved()) { break; } if (outSize == out.size()) { if (oldInputLength == in.readableBytes()) { break; } else { continue; } } if (oldInputLength == in.readableBytes()) { throw new DecoderException( StringUtil.simpleClassName(getClass()) + ".decode() did not read anything but decoded a message."); } if (isSingleDecode()) { break; } } } catch (DecoderException e) { throw e; } catch (Exception cause) { throw new DecoderException(cause); } } final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { // 设置解码器状态为正在解码,避免解码过程中另一个线程调用了 handlerRemoved 把数据销毁,造成混乱 decodeState = STATE_CALLING_CHILD_DECODE; try { decode(ctx, in, out); } finally { // STATE_HANDLER_REMOVED_PENDING 表示在解码过程中,ctx 被移除,需要由当前线程来调用 handlerRemoved boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING; decodeState = STATE_INIT; if (removePending) { handlerRemoved(ctx); } } } // 具体的消息解码算法,交给子类实现 protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
FixedLengthFrameDecoder
上一节我们研究了 ByteToMessageDecoder,本节研究其最简单的一个实现类 FixedLengthFrameDecoder。
该类核心是 decode 方法,当可读字节数据大于 frameLength 时,截取前 frameLength 个字节为一个 ByteBuf,存入列表 out 中。
public class FixedLengthFrameDecoder extends ByteToMessageDecoder { private final int frameLength; public FixedLengthFrameDecoder(int frameLength) { if (frameLength <= 0) { throw new IllegalArgumentException( "frameLength must be a positive integer: " + frameLength); } this.frameLength = frameLength; } @Override protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); } } protected Object decode( @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception { if (in.readableBytes() < frameLength) { return null; } else { return in.readRetainedSlice(frameLength); } }}
- Netty 权威指南笔记(七):ChannelPipeline 和 ChannelHandler 源码分析
- [netty核心类]--ChannelPipeline和ChannelHandler源码分析
- 自顶向下深入分析Netty(七)--ChannelPipeline和ChannelHandler总述
- Netty学习笔记(三) ChannelPipeline和ChannelHandler
- Netty(二)ChannelPipeline和ChannelHandler
- netty源码学习三(channel、channelHandler、channelPipeline)
- Netty in action—ChannelHandler和ChannelPipeline
- Netty实战读书笔记二:ChannelHandler和ChannelPipeline
- Netty源码分析:ChannelPipeline
- Netty 之 ChannelHandler,ChannelHandlerContext,ChannelPipeline
- 自顶向下深入分析Netty(七)--ChannelPipeline源码实现
- netty源码分析之ChannelPipeline
- netty源码分析之ChannelHandler
- netty源码分析之ChannelPipeline
- Netty in Action (十五) 第六章节 第一部分 ChannelHandler和ChannelPipeline
- 《Netty in Action》中文版—第六章 ChannelHandler和ChannelPipeline
- ChannelHandler和ChannelPipeline
- 一起学Netty(四)之 ChannelHandler,ChannelHandlerContext,ChannelPipeline
- 去除mysql中字段的重复值,去重操作
- Android:手把手带你入门神秘的 Rxjava
- mysql监控工具
- 机器学习笔记(6)算法可行性研究
- numpy reshape
- Netty 权威指南笔记(七):ChannelPipeline 和 ChannelHandler 源码分析
- 共享汽车行业迎来新玩家,美团、摩拜相继入局
- AJAX的多种用法
- MyBatis的foreach语句详解
- nginx实现多个tomcat负载均衡
- 06 H5直接使用网络文件系统
- 设计模式学习总结:单例模式(Singleton)
- oracle管道函数的用法
- ARM交叉编译工具链百度云盘下载地址