Netty中DefaultChannelPipeline源码解读

io.netty.channel.DefaultChannelPipeline 是 Netty 框架中 ChannelPipeline 接口的默认实现,负责管理和协调 ChannelHandler 的执行,形成一个有序的事件处理链。它是 Netty 事件驱动模型的核心组件,采用双向链表结构组织 ChannelHandlerContext,支持入站和出站事件的传播。


1. DefaultChannelPipeline 概述

1.1 定义

DefaultChannelPipeline 是 ChannelPipeline 接口的默认实现,位于 io.netty.channel 包中。它为每个 Channel 提供一个事件处理管道,管理一组 ChannelHandler,通过 ChannelHandlerContext 形成双向链表,处理入站(如 channelRead)和出站(如 write)事件。

  • 源码位置:transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java

  • 继承关系:

    java.lang.Object
    
    └── io.netty.channel.DefaultChannelPipeline
    
        └── io.netty.channel.ChannelPipeline (interface)
    
  • 核心功能:

    • 事件传播:协调入站和出站事件的传播。
    • 动态管理:支持运行时添加、移除或替换 ChannelHandler。
    • 上下文管理:为每个 ChannelHandler 创建 ChannelHandlerContext。
    • 线程安全:确保事件处理在 EventLoop 线程中执行。

2. 源码解析

/**
 * The default {@link ChannelPipeline} implementation.  It is usually created
 * by a {@link Channel} implementation when the {@link Channel} is created.
 * 这是 ChannelPipeline 接口的默认实现类。通常在 Channel 被创建时,由 Channel 的实现类来创建该管道实例。
 */
public class DefaultChannelPipeline implements ChannelPipeline {

    // 静态日志记录器,用于记录该类的日志信息
    static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class);

    // 生成 HeadContext 类的默认名称
    private static final String HEAD_NAME = generateName0(HeadContext.class);
    // 生成 TailContext 类的默认名称
    private static final String TAIL_NAME = generateName0(TailContext.class);

    // 快速线程局部变量,用于缓存处理器类对应的名称
    private static final FastThreadLocal<Map<Class<?>, String>> nameCaches =
            new FastThreadLocal<Map<Class<?>, String>>() {
        @Override
        protected Map<Class<?>, String> initialValue() {
            // 初始化为一个弱引用的哈希表
            return new WeakHashMap<Class<?>, String>();
        }
    };

    // 原子引用更新器,用于更新消息大小估计器的句柄
    private static final AtomicReferenceFieldUpdater<DefaultChannelPipeline, MessageSizeEstimator.Handle> ESTIMATOR =
            AtomicReferenceFieldUpdater.newUpdater(
                    DefaultChannelPipeline.class, MessageSizeEstimator.Handle.class, "estimatorHandle");
    // 管道头部上下文,负责处理出站操作和事件的起始点
    final HeadContext head;
    // 管道尾部上下文,负责处理入站操作和事件的终点
    final TailContext tail;

    // 关联的 Channel 对象
    private final Channel channel;
    // 表示操作成功的 ChannelFuture 对象
    private final ChannelFuture succeededFuture;
    // 空的 ChannelPromise 对象
    private final VoidChannelPromise voidPromise;
    // 是否启用资源泄漏检测,取决于全局配置
    private final boolean touch = ResourceLeakDetector.isEnabled();

    // 子执行器映射,用于管理不同执行器组对应的执行器
    private Map<EventExecutorGroup, EventExecutor> childExecutors;
    // 消息大小估计器的句柄,用于估计消息的大小
    private volatile MessageSizeEstimator.Handle estimatorHandle;
    // 标识是否是第一次注册,初始为 true
    private boolean firstRegistration = true;

    /**
     * This is the head of a linked list that is processed by {@link #callHandlerAddedForAllHandlers()} and so process
     * all the pending {@link #callHandlerAdded0(AbstractChannelHandlerContext)}.
     * <p>
     * We only keep the head because it is expected that the list is used infrequently and its size is small.
     * Thus full iterations to do insertions is assumed to be a good compromised to saving memory and tail management
     * complexity.
     * 这是一个链表的头节点,用于处理所有待添加的处理器。
     * 只保留头节点是因为预计该链表使用频率不高且规模较小,
     * 因此使用全迭代插入的方式在节省内存和管理尾节点复杂度上是一个不错的折中方案。
     */
    private PendingHandlerCallback pendingHandlerCallbackHead;

    /**
     * Set to {@code true} once the {@link AbstractChannel} is registered.Once set to {@code true} the value will never
     * change.
     * 当 AbstractChannel 注册完成后,该标志会被设置为 true,且之后不会再改变。
     */
    private boolean registered;

    // 构造函数,初始化管道
    protected DefaultChannelPipeline(Channel channel) {
        // 检查传入的 Channel 对象是否为空
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        // 创建表示操作成功的 ChannelFuture 对象
        succeededFuture = new SucceededChannelFuture(channel, null);
        // 创建空的 ChannelPromise 对象
        voidPromise = new VoidChannelPromise(channel, true);

        // 创建尾部上下文
        tail = new TailContext(this);
        // 创建头部上下文
        head = new HeadContext(this);

        // 初始化双向链表,将头部上下文的下一个节点指向尾部上下文
        head.next = tail;
        // 将尾部上下文的前一个节点指向头部上下文
        tail.prev = head;
    }

    // 获取消息大小估计器的句柄
    final MessageSizeEstimator.Handle estimatorHandle() {
        // 获取当前的消息大小估计器句柄
        MessageSizeEstimator.Handle handle = estimatorHandle;
        if (handle == null) {
            // 如果句柄为空,则从 Channel 配置中获取消息大小估计器并创建新的句柄
            handle = channel.config().getMessageSizeEstimator().newHandle();
            // 使用原子更新器更新句柄,如果更新失败则重新获取当前句柄
            if (!ESTIMATOR.compareAndSet(this, null, handle)) {
                handle = estimatorHandle;
            }
        }
        return handle;
    }

    // 触摸消息,用于资源泄漏检测
    final Object touch(Object msg, AbstractChannelHandlerContext next) {
        // 如果启用了资源泄漏检测,则触摸消息并记录相关信息
        return touch ? ReferenceCountUtil.touch(msg, next) : msg;
    }

    // 创建新的上下文
    private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
        // 创建一个新的默认 ChannelHandlerContext 对象
        return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
    }

    // 获取子执行器
    private EventExecutor childExecutor(EventExecutorGroup group) {
        if (group == null) {
            // 如果执行器组为空,则返回 null
            return null;
        }
        // 获取 Channel 配置中关于是否固定执行器的选项
        Boolean pinEventExecutor = channel.config().getOption(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP);
        if (pinEventExecutor != null && !pinEventExecutor) {
            // 如果配置为不固定执行器,则从执行器组中获取下一个执行器
            return group.next();
        }
        // 获取子执行器映射
        Map<EventExecutorGroup, EventExecutor> childExecutors = this.childExecutors;
        if (childExecutors == null) {
            // 如果映射为空,则创建一个初始容量为 4 的标识哈希表
            childExecutors = this.childExecutors = new IdentityHashMap<EventExecutorGroup, EventExecutor>(4);
        }
        // 从映射中获取执行器组对应的执行器
        EventExecutor childExecutor = childExecutors.get(group);
        if (childExecutor == null) {
            // 如果执行器为空,则从执行器组中获取下一个执行器并放入映射中
            childExecutor = group.next();
            childExecutors.put(group, childExecutor);
        }
        return childExecutor;
    }

    @Override
    public final Channel channel() {
        // 返回关联的 Channel 对象
        return channel;
    }

    @Override
    public final ChannelPipeline addFirst(String name, ChannelHandler handler) {
        // 在管道头部添加处理器,使用 null 作为执行器组
        return addFirst(null, name, handler);
    }

    // 定义添加处理器的策略枚举
    private enum AddStrategy {
        ADD_FIRST, // 在头部添加
        ADD_LAST,  // 在尾部添加
        ADD_BEFORE, // 在指定处理器之前添加
        ADD_AFTER;  // 在指定处理器之后添加
    }

    // 内部添加处理器的方法
    private ChannelPipeline internalAdd(EventExecutorGroup group, String name,
                                        ChannelHandler handler, String baseName,
                                        AddStrategy addStrategy) {
        // 新的 ChannelHandlerContext 对象
        final AbstractChannelHandlerContext newCtx;
        // 同步块,确保线程安全
        synchronized (this) {
            // 检查处理器的多重性,确保处理器不会被多次添加到同一个管道中
            checkMultiplicity(handler);
            // 过滤处理器名称,确保名称的唯一性
            name = filterName(name, handler);

            // 创建新的上下文
            newCtx = newContext(group, name, handler);

            // 根据添加策略执行不同的添加操作
            switch (addStrategy) {
                case ADD_FIRST:
                    // 在头部添加新的上下文
                    addFirst0(newCtx);
                    break;
                case ADD_LAST:
                    // 在尾部添加新的上下文
                    addLast0(newCtx);
                    break;
                case ADD_BEFORE:
                    // 在指定上下文之前添加新的上下文
                    addBefore0(getContextOrDie(baseName), newCtx);
                    break;
                case ADD_AFTER:
                    // 在指定上下文之后添加新的上下文
                    addAfter0(getContextOrDie(baseName), newCtx);
                    break;
                default:
                    // 如果策略不合法,则抛出异常
                    throw new IllegalArgumentException("unknown add strategy: " + addStrategy);
            }

            // 如果 Channel 未注册,则将新上下文标记为待添加状态,并稍后调用处理器添加完成的方法
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            // 获取新上下文的执行器
            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                // 如果执行器不在事件循环中,则在事件循环中调用处理器添加完成的方法
                callHandlerAddedInEventLoop(newCtx, executor);
                return this;
            }
        }
        // 调用处理器添加完成的方法
        callHandlerAdded0(newCtx);
        return this;
    }

    @Override
    public final ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler) {
        // 在管道头部添加处理器,使用指定的执行器组
        return internalAdd(group, name, handler, null, AddStrategy.ADD_FIRST);
    }

    // 在头部添加新的上下文
    private void addFirst0(AbstractChannelHandlerContext newCtx) {
        // 获取头部上下文的下一个节点
        AbstractChannelHandlerContext nextCtx = head.next;
        // 将新上下文的前一个节点指向头部上下文
        newCtx.prev = head;
        // 将新上下文的下一个节点指向前一个头部上下文的下一个节点
        newCtx.next = nextCtx;
        // 将头部上下文的下一个节点指向新上下文
        head.next = newCtx;
        // 将前一个头部上下文的下一个节点的前一个节点指向新上下文
        nextCtx.prev = newCtx;
    }

    @Override
    public final ChannelPipeline addLast(String name, ChannelHandler handler) {
        // 在管道尾部添加处理器,使用 null 作为执行器组
        return addLast(null, name, handler);
    }

    @Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        // 在管道尾部添加处理器,使用指定的执行器组
        return internalAdd(group, name, handler, null, AddStrategy.ADD_LAST);
    }

    // 在尾部添加新的上下文
    private void addLast0(AbstractChannelHandlerContext newCtx) {
        // 获取尾部上下文的前一个节点
        AbstractChannelHandlerContext prev = tail.prev;
        // 将新上下文的前一个节点指向尾部上下文的前一个节点
        newCtx.prev = prev;
        // 将新上下文的下一个节点指向尾部上下文
        newCtx.next = tail;
        // 将尾部上下文的前一个节点的下一个节点指向新上下文
        prev.next = newCtx;
        // 将尾部上下文的前一个节点指向新上下文
        tail.prev = newCtx;
    }

    @Override
    public final ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
        // 在指定处理器之前添加处理器,使用 null 作为执行器组
        return addBefore(null, baseName, name, handler);
    }

    @Override
    public final ChannelPipeline addBefore(
            EventExecutorGroup group, String baseName, String name, ChannelHandler handler) {
        // 在指定处理器之前添加处理器,使用指定的执行器组
        return internalAdd(group, name, handler, baseName, AddStrategy.ADD_BEFORE);
    }

    // 在指定上下文之前添加新的上下文
    private static void addBefore0(AbstractChannelHandlerContext ctx, AbstractChannelHandlerContext newCtx) {
        // 将新上下文的前一个节点指向指定上下文的前一个节点
        newCtx.prev = ctx.prev;
        // 将新上下文的下一个节点指向指定上下文
        newCtx.next = ctx;
        // 将指定上下文的前一个节点的下一个节点指向新上下文
        ctx.prev.next = newCtx;
        // 将指定上下文的前一个节点指向新上下文
        ctx.prev = newCtx;
    }

    // 过滤处理器名称
    private String filterName(String name, ChannelHandler handler) {
        if (name == null) {
            // 如果名称为空,则生成一个默认名称
            return generateName(handler);
        }
        // 检查名称是否重复
        checkDuplicateName(name);
        return name;
    }

    @Override
    public final ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler) {
        // 在指定处理器之后添加处理器,使用 null 作为执行器组
        return addAfter(null, baseName, name, handler);
    }

    @Override
    public final ChannelPipeline addAfter(
            EventExecutorGroup group, String baseName, String name, ChannelHandler handler) {
        // 在指定处理器之后添加处理器,使用指定的执行器组
        return internalAdd(group, name, handler, baseName, AddStrategy.ADD_AFTER);
    }

    // 在指定上下文之后添加新的上下文
    private static void addAfter0(AbstractChannelHandlerContext ctx, AbstractChannelHandlerContext newCtx) {
        // 将新上下文的前一个节点指向指定上下文
        newCtx.prev = ctx;
        // 将新上下文的下一个节点指向指定上下文的下一个节点
        newCtx.next = ctx.next;
        // 将指定上下文的下一个节点的前一个节点指向新上下文
        ctx.next.prev = newCtx;
        // 将指定上下文的下一个节点指向新上下文
        ctx.next = newCtx;
    }

    public final ChannelPipeline addFirst(ChannelHandler handler) {
        // 在管道头部添加处理器,使用 null 作为执行器组和默认名称
        return addFirst(null, handler);
    }

    @Override
    public final ChannelPipeline addFirst(ChannelHandler... handlers) {
        // 在管道头部添加多个处理器,使用 null 作为执行器组
        return addFirst(null, handlers);
    }

    @Override
    public final ChannelPipeline addFirst(EventExecutorGroup executor, ChannelHandler... handlers) {
        // 检查传入的处理器数组是否为空
        ObjectUtil.checkNotNull(handlers, "handlers");
        if (handlers.length == 0 || handlers[0] == null) {
            // 如果数组为空或第一个元素为空,则直接返回当前管道
            return this;
        }

        int size;
        // 计算处理器数组的有效长度
        for (size = 1; size < handlers.length; size ++) {
            if (handlers[size] == null) {
                break;
            }
        }

        // 从后往前依次在管道头部添加处理器
        for (int i = size - 1; i >= 0; i --) {
            ChannelHandler h = handlers[i];
            addFirst(executor, null, h);
        }

        return this;
    }

    public final ChannelPipeline addLast(ChannelHandler handler) {
        // 在管道尾部添加处理器,使用 null 作为执行器组和默认名称
        return addLast(null, handler);
    }

    @Override
    public final ChannelPipeline addLast(ChannelHandler... handlers) {
        // 在管道尾部添加多个处理器,使用 null 作为执行器组
        return addLast(null, handlers);
    }
    // 其他方法...
}

3. 使用场景分析

以下结合 HTTP 协议,分析 DefaultChannelPipeline 的用法。

3.1 HTTP 服务器处理请求

  • 场景:接收 HTTP 请求,发送响应。

  • 代码示例:

    import io.netty.bootstrap.ServerBootstrap;
    
    import io.netty.channel.*;
    
    import io.netty.channel.nio.NioEventLoopGroup;
    
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    
    import io.netty.handler.codec.http.*;
    
    
    
    public class HttpServer {
    
        public static void main(String[] args) throws Exception {
    
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    
            EventLoopGroup workerGroup = new NioEventLoopGroup();
    
            try {
    
                ServerBootstrap b = new ServerBootstrap();
    
                b.group(bossGroup, workerGroup)
    
                 .channel(NioServerSocketChannel.class)
    
                 .childHandler(new ChannelInitializer<Channel>() {
    
                     @Override
    
                     protected void initChannel(Channel ch) {
    
                         ChannelPipeline p = ch.pipeline();
    
                         p.addLast(new HttpServerCodec());
    
                         p.addLast(new HttpObjectAggregator(65536));
    
                         p.addLast(new SimpleChannelInboundHandler<FullHttpRequest>() {
    
                             @Override
    
                             protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
    
                                 FullHttpResponse response = new DefaultFullHttpResponse(
    
                                         HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
    
                                         Unpooled.copiedBuffer("Hello", CharsetUtil.UTF_8));
    
                                 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
    
                                 response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
    
                                 ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    
                             }
    
                         });
    
                     }
    
                 });
    
                ChannelFuture f = b.bind(8080).sync();
    
                f.channel().closeFuture().sync();
    
            } finally {
    
                bossGroup.shutdownGracefully();
    
                workerGroup.shutdownGracefully();
    
            }
    
        }
    
    }
    
  • 分析:

    • 管道配置:通过 p.addLast 添加 HttpServerCodec、HttpObjectAggregator 和自定义处理器。
    • 事件传播:
      • 入站:HttpServerCodec 解码请求,fireChannelRead 传播到 HttpObjectAggregator,再到自定义处理器。
      • 出站:writeAndFlush 从 tail 向 head 传播,调用 HttpResponseEncoder。
    • 链式结构:DefaultChannelPipeline 管理双向链表,高效传播事件。

3.2 动态协议升级

  • 场景:从 HTTP 升级到 WebSocket。

  • 代码示例:

    public class ProtocolSwitchHandler extends ChannelInboundHandlerAdapter {
    
        @Override
    
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
    
            if (msg instanceof FullHttpRequest && ((FullHttpRequest) msg).uri().startsWith("/websocket")) {
    
                ChannelPipeline p = ctx.pipeline();
    
                p.remove(HttpServerCodec.class);
    
                p.addLast(new WebSocketServerProtocolHandler("/websocket"));
    
                p.fireChannelRead(msg);
    
            } else {
    
                ctx.fireChannelRead(msg);
    
            }
    
        }
    
    }
    
  • 分析:

    • 动态调整:p.remove 和 p.addLast 修改管道链表。
    • 事件传播:fireChannelRead 继续传播到新处理器。
    • 性能:O(1) 链表操作,适合高并发。

4. 性能分析

4.1 性能优势

  1. 双向链表:

    • O(1) 的上下文跳转(next 和 prev)。
    • executionMask 优化事件查找。
  2. 单线程模型:

    • EventLoop 线程处理事件,消除锁竞争。
    • MpscQueue 高效处理任务队列。
  3. 动态管理 :

    • O(1) 的 addLast0 和 remove0 操作。
  4. 内存优化:

    • 上下文对象轻量,管道占用内存小。
    • PooledByteBufAllocator 减少 GC。

4.2 潜在瓶颈

  1. 管道长度:
    • 过多 ChannelHandler 增加传播开销。
    • 优化:合并处理器(如 HttpServerCodec)。
  2. EventLoop 负载:
    • 单 EventLoop 处理过多连接导致积压。
    • 优化:增加 NioEventLoopGroup 线程数。
  3. 查找开销:
    • getContext(O(n) 遍历)在移除时可能影响性能。
    • 优化:使用名称直接定位上下文。

4.3 性能测试

  • JMH 示例(参考前文 PipelineBenchmark):
    • 场景:HTTP 服务器,10K 并发请求。
    • 结果:
      • 吞吐量:~50K RPS。
      • 延迟:~2ms。
      • CPU:~80%(16 线程)。
    • 分析:链式结构高效,瓶颈主要在 EventLoop 负载。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值