Netty中DefaultChannelPipeline源码解读

工程伦理ppt 工程伦理学 第一章 工程与伦理 1.1 如何理解工程 一、技术与工程的区别 二、技术与工程的联系 三、工程的定义 五、工程具有不确定性和探索性 六、理解工程活动的7个维度 1.2 如何理解伦理 一、道德与伦理 二、不同的伦理立场 三、伦理困境与伦理选择 1.3 工程实践中的伦理问题 一、工程活动中的行动者网络(具有动态性和网络性) 二、主要的工程伦理问题 三、工程伦理问题的特点 1.4 如何处理工程实践中的伦理问题 一、工程实践中伦理问题的辨识 二、处理工程伦理问题的基本原则 三、应对工程伦理问题的基本思路 阅读详情

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 负载。
Faster Planner——Kinodynamic Astar详解 实际成本和启发式成本 实际成本 Actual Cost   每条轨迹的真实代价函数定义如下: J(T)=∫0T∥u(t)∥2dt+ρT \mathcal{J}(T)=\int_{0}^{T}\|\mathbf{u}(t)\|^{2} d t+\rho T J(T)=∫0T​∥u(t)∥2dt+ρT 离散形式下: 启发成本 Heuristic Cost pμ∗(t)=16αμt3+12βμt2+vμc+pμc=pμc+vμc+T2∗((2∗vμc−2∗vμg)/(2∗T)−(6∗pμc−6∗pμg+6∗T∗v 阅读详情

相关推荐

使用moveit!控制真实机械臂(5)——编写真实机械臂节点中的action服务程序

上一篇文章中,我们简要介绍了ROS中action的基本知识,为了控制真实机械臂,我们已经修改了moveit配置文件,使得moveti启动后具备一个control_msgs::FollowJointTrajectoryAction类型的action客户端,接下来,我们完成该类型action的另一半,编写C++程序来实现action的服务端。由于底层机器人控制程序一般差别都很大,这里展示的只是编程要点...

爱学习的草莓熊 1万+

Netty报错:io.netty.channel.DefaultChannelPipeline onUnhandledInboundException

十一月 14, 2019 7:48:36 下午 io.netty.channel.DefaultChannelPipeline onUnhandledInboundException 警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the l...

屑狐狸 1万+

基于Unidbg的小红书加密协议动态分析与算法还原实战

在移动应用安全领域,加密协议与签名机制是保障数据传输安全、实现客户端身份验证的核心技术。其原理通常涉及哈希算法、对称/非对称加密以及自定义的混淆逻辑,旨在防止请求被篡改或重放。理解这些协议对于安全研究、风控策略制定及合规的数据交互分析具有重要价值。在实际的工程实践中,面对高度混淆和加固的Native代码(SO库),传统的静态分析往往难以深入。此时,沙盒模拟执行技术提供了一种动态、可控的分析方案。通过搭建一个虚拟的移动应用运行环境,研究人员可以在无需真机、规避反调试机制的情况下,完整地追踪和观察加密算法的执行

weixin_29315091的博客 327

Java-IO系列:Netty中DefaultChannelPipeline

类图 DefaultChannelPipeline是一个Channel中用于处理各种入,出事件的流水线。 DefaultChannelPipeline内部通过双向链表维护了一个pipeline中可以处理事件的处理器列表。每个处理器都是一个AbstractChannelHandlerContext的子类。AbstractChannelHandlerContext中有两个链表相关的属性,prev和next,分别代表该节点的前后相邻节点。 而AbstractChannelHandlerContext有三个子类。分

少年不识愁滋味 2007

安时积分法和开路电压法结合估算锂电池SOC

基于安时积分法和开路电压法结合的锂电池SOC估计Simulink仿真建模,利用安时积分法计算放电过程中的SOC,在搁置状态利用开路电压法进行SOC修正。

qq_39304296的博客 2559

基础知识学习 -- qnx 系统

qnx os车载系统

求变,从思想开始 3877

Netty4事件处理传播机制,最新大厂程序员进阶宝典

从这里看一个Channel对应一个ChannelPipeline。 2.1 事件链构建 本节将以addFirst方法为例展示ChannelPipeline事件链的维护实现。 DefaultChannelPipeline#addFirst public final ChannelPipeline addFirst(String name, ChannelHandler handler) { return addFirst(null, name, handler); } 内部调用其重载方法。接下来重点分析该方法

m0_64867220的博客 153

准备生日礼物

摘要: 题目要求统计某月份需要准备的员工生日礼物数量。输入包括目标月份、员工姓名列表和对应的生日列表(格式为Year/Month/Day)。需处理重复员工(以最后一次录入为准),统计该月生日的员工人数。示例1中5月有3名员工生日,输出3;示例2中10月有6名,输出6。解题关键在于使用哈希表去重(以员工姓名为键),拆分生日字符串提取月份进行匹配。代码实现(C++/Java/Python)均遵循以下步骤:读取输入、哈希去重、解析月份并统计目标月份人数。时间复杂度为O(n),空间复杂度O(n)。

面试高手的博客 300

netty pipeline 执行顺序解读

最近在做netty 部标开发,顺便研究了下pipeline执行顺序,发现中文博客,网站讲的都不太透彻。 自己结合网上例子,实验,再最后看源码,总结下,希望能讲的透彻。 借用网上博客一个例子https://blog.youkuaiyun.com/u013252773/article/details/21195593: 如添加pipelinehandler顺序是: pipeline.addLast(new...

nightrainljh的专栏 4278

【系统架构设计师】2026年上半年真题论文:论高并发系统的设计与实践

本文围绕高并发系统架构设计展开,重点论述了电商秒杀系统的实战经验。文章采用"痛点-解决方案-效果"的逻辑结构,从缓存设计、异步处理、限流降级、数据库优化等6个维度详细阐述了应对高并发的技术策略。其中,重点介绍了多级缓存体系、基于消息队列的异步处理机制和微服务拆分实践,并辅以实际案例说明如何解决超卖、缓存雪崩等典型问题。全文强调高并发设计需要系统性思维,通过空间换时间、异步换吞吐、横向换能力等原则,最终实现系统吞吐量从500TPS提升至20000TPS的性能突破。

数据知道的博客 1160

2025华为杯第二十二届中国研究生数学建模竞赛C题:围岩裂隙精准识别与三维模型重构 - 完整解题分析+完整MATLAB源码!(下篇)

🎓 本文收录于 《滚雪球学数学建模》专栏,本专栏专注于从零基础出发,带你循序渐进掌握数学建模的核心方法与思维路径。通过“滚雪球式”学习法,从最简单的生活化案例到经典建模工具,再到省赛、国赛真题实战演练,逐步积累知识与技能,帮助你搭建完整的建模体系。无论你是刚接触建模的新生,还是希望提升科研与竞赛能力的学者与工程师,本专栏都将为你提供一条清晰、高效、可实操的建模成长路线,助力你快速进阶,突破难点,迈向数学建模竞赛与实际应用的更高峰 🚀!

**My Coding Family** 1621

吃透Netty源码系列二之ServerBootstrap

Netty源码深度解析系列二启动器ServerBootstrap关键的binddoBind通道的创建和初始化initAndRegister实例化NioServerSocketChannelDefaultChannelPipeline初始化通道init(channel)关键点addLast创建`ChannelHandlerContext`实例newContext不是增加到尾部的addLast0 启动...

王伟王胖胖的博客 1954

在Centos上从零开始部署并运行TinyWebServer服务器,过程记录(非常详细)

TinyWebServer是个什么项目?它是Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器。使用线程池+非阻塞socket+epoll(ET和LT均实现) +事件处理(Reactor和模拟Proactor均实现) 的并发模型使用状态机解析HTTP请求报文,支持解析GET和POST请求访问服务器数据库实现web端用户注册、登录功能,可以请求服务器图片和视频文件实现同步/异步日志系统,记录服务器运行状态。......

AnChenliang_1002的博客 3021

【Netty源码解读和权威指南】第16篇:Netty Pipeline源码解析(上)—— 玩转Handler链的秘密

本文摘要:深入解析Netty Pipeline设计原理与实现,对比传统责任链模式,展示其双向处理、动态修改等优势。通过支付系统重构案例,演示Pipeline如何解耦复杂业务逻辑。详解Pipeline继承体系、核心API(添加/删除Handler)及事件传播机制,提供完整代码示例,帮助开发者掌握Netty核心组件的灵活运用。

xyghehehehe的专栏 84

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

netty源码分析(十三)Netty核心四大组件关系与构建方式深度解读

上一节主要看了一下ChannelHandlerContext,中间我们走到DefaultChannelPipeline的addLast方法,这一节我们从addLast方法切入: 其中group为null,name为null public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHan

wzq6578702的专栏 1003

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

netty中的Channel、ChannelPipeline

一、Channel与ChannelPipeline关系 每一个新创建的 Channel 都将会被分配一个新的ChannelPipeline。这项关联是永久性 的;Channel 既不能附加另外一个 ChannelPipeline,也不能分离其当前的。在 Netty 组件 的生命周期中,这是一项固定的操作,不需要开发人员的任何干预。 在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应, 它们的组成关系如下: 通过上图我们可以看到, 一个 Channel 包含了一

码上代码的博客 9015

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Netty源码解读(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的“梦境”概念,希望大家喜欢。 一层梦境:Channel实现概览 在Netty里,Channel是通讯的载体,而ChannelHandler负责Channel中的逻辑处理。

kobejayandy的专栏 2414

netty 源码解析一:ServerBootstrap 服务端

ServerBootstrap 服务启动类 继承自 AbstractBootstrap group(EventLoopGroup) 赋值 EventLoopGroup (创建 NioEventLoopGroup) 线程组(池) channel(Class) 赋值 ChannelFactory (通过class的构造器,创建 Re...

Pazz 517

netty4.0.x源码分析—ChannelPipeline

备注:本文的分析基于netty 4.0.9final版本 1、ChannelPipeline结构图 2、关键类和接口分析 上一篇关于Channel的文章,在AbstractChannel的介绍中,以及提到了pipeline,这是操作处理的入口,是一个比较重要的概念,这里有必要对pipeline分析一下。 1)ChannelInboundInvoker /** * Inter

pingnanlee的专栏 8341

Netty 出站入站机制详解

前段时间在B站学习Netty,讲到出站入站的机制时一直没搞明白到底是怎么弄的,直到开始讲源码部分时才终于搞明白。 先来看看Netty官方有关出站入站机制的解释: 按照图片的理解,则是在通道中,每次出现读事件时,会从头至尾依次调用Inbound即入站方法处理; 而触发写事件时,则会从尾到头依次调用outbound即出站方法处理。 这里会给人一种错觉,那就是netty在内部维护了两个单向链表实现出站入站操作,而事实上,ChannelPipeline中实际上是维护了一条由ChannelHandlerCo

男儿当自强 1663

Netty源码分析第2章(NioEventLoop)---->第1节: NioEventLoopGroup之创建线程执行器

Netty源码分析第2章(NioEventLoop)---->第1节: NioEventLoopGroup之创建线程执行器 Netty源码分析第二章: NioEventLoop 概述: 通过上一章的学习, 我们了解了Server启动的大致流程, 有很多组件与模块并没有细讲, 从这个章开始, 我们开始详细剖析...

weixin_30341735的博客 378

01NioSocketChannel笔记

1.在NioSocketChannel里面直接从java的SelectorProvider里面拿到SelectorProvider private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();2.在创建NioSocketChannel的时候会创建一个, N...

weixin_30845171的博客 97

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 303

第三篇 第 6 课

前置:第三篇第 1 课(stdx 引入),第二篇第 1 课(std.regex)。环境:仓颉 SDK 1.2.0。本文所有代码与输出均逐行实测。

weixin_41618057的博客 527

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 355

信创环境下PageOffice文档控件适配与实践

信创适配做到文档控件这一层,拼的已经不是"有没有国产版本",而是同一个功能在两类终端上是否真的等价。等价的标准很具体:同一次点击能得到同样的结果,同一份公文能得到同样的版式,同一个项目不需要维护两套代码。这背后是三件并不轻松的工作:服务端用一套接口同时支撑两类终端,客户端把两种系统与四种芯片的安装包分发清楚,文档内核层由产品侧跟着 WPS 的版本走。浏览器这一层的压力,则被收进了产品自己的窗口里——升级节奏由产品掌握,不再交给运气。

zi_wu_xian的专栏 332
上一篇: Netty中AbstractChannelHandlerContext源码分析
下一篇: Netty中 ? extends Future<? super V>这种的写法的理解
Jooou
博客等级 码龄9年 226粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值