文章目录

  • 一、Netty逻辑架构【上】
    • 1.1 网络通信层
      • 1.1.1 BootStrap & ServerBootStrap
        • 1. ✅核心方法链与配置
        • 2. ✅ 架构与流程
        • 3. ✅ 底层实现与原理分析
        • 4. ✅ 实践经验与总结
      • 1.1.2 Channel
    • 1.2 事件调度层
      • 1.2.1 事件调度层概述
      • 1.2.2 EventLoop【事件循环】
      • 1.2.3 EventLoopGroup【事件循环组】
      • 1.2.4 底层实现原理
      • 1.2.5 最佳实践与经验总结


推荐阅读:

【01】Netty从0到1系列之I/O模型
【02】Netty从0到1系列之NIO
【03】Netty从0到1系列之Selector
【04】Netty从0到1系列之Channel
【05】Netty从0到1系列之Buffer(上)
【06】Netty从0到1系列之Buffer(下)
【07】Netty从0到1系列之零拷贝技术
【08】Netty从0到1系列之整体架构、入门程序
【09】Netty从0到1系列之EventLoop
【10】Netty从0到1系列之EventLoopGroup
【11】Netty从0到1系列之Future
【12】Netty从0到1系列之Promise
【13】Netty从0到1系列之Netty Channel
【14】Netty从0到1系列之ChannelFuture
【15】Netty从0到1系列之CloseFuture
【16】Netty从0到1系列之Netty Handler
【17】Netty从0到1系列之Netty Pipeline【上】
【18】Netty从0到1系列之Netty Pipeline【下】
【19】Netty从0到1系列之Netty ByteBuf【上】
【20】Netty从0到1系列之Netty ByteBuf【中】
【21】Netty从0到1系列之Netty ByteBuf【下】


一、Netty逻辑架构【上】

在这里插入图片描述

Netty 的逻辑处理架构为典型网络分层架构设计:

  • 网络通信层
  • 事件调度层
  • 服务编排层

1.1 网络通信层

网络通信层的职责是执行网络 I/O 的操作。它支持多种网络协议和 I/O 模型的连接操作。当网络数据读取到内核缓冲区后,会触发各种网络事件,这些网络事件会分发给事件调度层进行处理。

网络通信层的核心组件包含BootStrap、ServerBootStrap、Channel三个组件

1.1.1 BootStrap & ServerBootStrap

Bootstrap 是引导的意思,它主要负责整个 Netty 程序的启动、初始化、服务器连接等过程,它相当于一条主线,串联了 Netty 的其他核心组件。

  • 用于客户端引导的 Bootstrap
  • 用于服务端引导的 ServerBootStrap

在这里插入图片描述

在 Netty 中,所有网络应用的启动都是通过 Bootstrap(客户端)或 ServerBootstrap(服务端)来完成的。它们是工厂类和配置容器的结合体,负责创建、配置并最终启动 Channel

  • Bootstrap: 用于客户端。它只需要一个 EventLoopGroup,因为它只需要创建一个单独的 Channel 来与远程服务器进行通信。
  • ServerBootstrap: 用于服务端。它需要两个 EventLoopGroup(通常如此),一个通常被称为 “Boss”,负责接收传入的连接;另一个被称为 “Worker”,负责处理已被接受的连接的流量。
类名用途对应场景
Bootstrap用于启动 Netty 客户端发起 TCP 连接(如 HTTP 客户端、RPC 调用方)
ServerBootstrap用于启动 Netty 服务端监听端口,接收客户端连接(如 Web 服务器、RPC 服务提供方)

本质:它们是 Netty 的“启动器”,封装了复杂的 NIO 套接字创建、线程模型配置、Channel 初始化等流程,提供流畅式 API(Fluent API),极大简化了网络编程。

1. ✅核心方法链与配置

两者都通过流畅的链式调用(Fluent API)进行配置,核心方法包括:

  • .group(): 设置 EventLoopGroup
  • .channel(): 指定要实例化的 Channel 类(如 NioSocketChannel, NioServerSocketChannel)。
  • .handler(): 为 BootstrapServerBootstrap 本身的 Channel 设置一个 ChannelHandler
  • .childHandler(): (ServerBootstrap 特有) 为 ServerBootstrap 接收的 子 Channel(即代表客户端连接的 SocketChannel)设置 ChannelHandler
  • .option(): 为 BootstrapServerBootstrap 本身的 Channel 设置底层 TCP 参数。
  • .childOption(): (ServerBootstrap 特有) 为 ServerBootstrap 接收的 子 Channel 设置底层 TCP 参数。
2. ✅ 架构与流程
  • ServerBootstrapBootstrap 的启动流程及其核心组件关系:
Bootstrap 启动流程
初始化
注册到
Pipeline配置
循环
.groupgroup
Bootstrap
.channelNioSocketChannel
.handler 用于Client Channel
.connecthost, port
NioSocketChannel
Client EventLoop
Client ChannelPipeline
包含handler里添加的处理器
Selector.select 处理READ/WRITE等
ServerBootstrap 启动流程
初始化
注册到
循环
有新的连接
注册到
Pipeline配置
循环
.groupbossGroup, workerGroup
ServerBootstrap
.channelNioServerSocketChannel
.handler 用于ServerChannel
.childHandler 用于子Channel
.bindport
NioServerSocketChannel
Boss EventLoop
Selector.select 等待ACCEPT
创建NioSocketChannel
Worker EventLoop
子ChannelPipeline
包含childHandler里添加的处理器
Selector.select 处理READ/WRITE等
3. ✅ 底层实现与原理分析

启动过程分析: ServerBootstrap.bind() 为例,其底层过程非常精妙

  • 创建和初始化Channel

    • 通过反射调用 channelFactory.newChannel() 创建 NioServerSocketChannel 实例。
    • 调用 init(channel) 方法初始化该 Channel。这是最关键的一步:
      • .option() 设置的参数应用到 Channel 的 config 上。
      • .handler() 设置的 ChannelHandler 添加到 Channel 的 Pipeline 中。
      • 在 Pipeline 的尾部自动添加一个 ServerBootstrapAcceptor。这个 Handler 是 Netty 内部的“连接接收器”
  • 注册到 EventLoop

    • 将初始化好的 NioServerSocketChannel 异步地注册到 BossGroup 中的一个 EventLoop 上。这个过程会提交一个任务到 EventLoop 的线程中执行。

    • 在 EventLoop 线程中,真正的注册操作是将 Java NIO 的 ServerSocketChannel 注册到 EventLoop 的 Selector 上,并设置兴趣操作为 OP_ACCEPT

  • 绑定端口

    • 注册成功后,继续在 EventLoop 线程中调用 Channelbind() 方法,最终调用底层 Java NIO 的 ServerSocketChannel.bind()

    • 绑定成功后,会触发 channelActive 事件,Pipeline 中的处理器会相应地将 OP_ACCEPT 事件再次注册到 Selector,确保开始接收连接。

  • 接收连接(ServerBootstrapAcceptor 的作用)

    • 当 BossGroup中的 EventLoop轮询到 OP_ACCEPT事件时,会调用 NioServerSocketChannel的 read() 方法,其内部通过 ServerSocketChannel.accept()创建一个 Java NIO 的 SocketChannel。
    • Netty 随后会包装这个 SocketChannel 为 NioSocketChannel。
    • 关键一步: ServerBootstrapAcceptor的 channelRead() 方法会被调用,这个 NioSocketChannel 作为参数传入。
    • ServerBootstrapAcceptor 会做以下工作:
      • .childOption().childAttr() 设置的参数应用到子 Channel。
      • .childHandler() 中设置的 ChannelInitializer 添加到子 Channel 的 Pipeline 中。
      • 将这个子 Channel 注册到 WorkerGroup 中的一个 EventLoop 上(负载均衡

至此,新连接的建立和处理职责就从 BossGroup 完全移交给了 WorkerGroup

Bootstrap.connect() 的过程类似,但更简单,因为它没有子 Channel 的概念,创建好的 NioSocketChannel 会直接注册到其唯一的 EventLoopGroup 上,然后发起连接操作。

4. ✅ 实践经验与总结
  1. 线程池配置
  • BossGroup 通常设置为 1。一个线程足以处理所有的连接接收请求,设置过多纯属浪费。
  • WorkerGroup 默认大小是 CPU核心数 * 2。这是一个很好的起点,应根据实际业务是 I/O 密集型 还是 CPU 密集型 进行调整。I/O 密集型可以调大一些。
  1. ChannelOption 配置
  • SO_BACKLOG (Server): 决定了完全建立连接队列的长度。在高并发场景下需要适当调大。
  • TCP_NODELAY (Client): 建议设置为 true,禁用 Nagle 算法,减少小数据包的发送延迟。
  • SO_KEEPALIVE (Child): 开启 TCP 层的心跳机制,保证连接存活。
  • SO_RCVBUF/SO_SNDBUF: 根据网络状况调整发送和接收缓冲区大小。
  1. 资源管理
  • 始终使用 shutdownGracefully(): 在 finally 块中优雅关闭 EventLoopGroup,它会等待任务执行完毕并释放资源。
  • ChannelInitializer 的使用: 在 initChannel 方法中添加 Handler 后,这个 ChannelInitializer 会将自己从 Pipeline 中移除,所以它可以被多个 Channel 安全共享。
  1. Handler 的组织
  • 在 ServerBootstrap 中,.handler() 用于添加处理服务端本身状态的处理器(如日志、监控),而 .childHandler() 用于添加处理业务逻辑的处理器。
  • 客户端的 .handler()等同于服务端的 .childHandler()

1.1.2 Channel

表示通道的意思,它是网络的载体.Channel提供了基本的 API 用于网络 I/O 操作,如 register、bind、connect、read、write、flush 等.

Netty 自己实现的 Channel 是以 JDK NIO Channel 为基础的,相比较于 JDK NIO,Netty 的 Channel 提供了更高层次的抽象,同时屏蔽了底层 Socket 的复杂性.

在这里插入图片描述

  • NioServerSocketChannel 异步 TCP 服务端
  • NioSocketChannel 异步 TCP 客户端
  • OioServerSocketChannel 同步 TCP 服务端。
  • OioSocketChannel 同步 TCP 客户端。
  • NioDatagramChannel 异步 UDP 连接。
  • OioDatagramChannel 同步 UDP 连接。

同时Channel还有很多种状态:

  • 连接建立
  • 连接注册
  • 数据读写
  • 连接销毁
  • ….

随着状态的变化,Channel 处于不同的生命周期,每一种状态都会绑定相应的事件回调

事件描述
channelRegisteredChannel 创建后被注册到 EventLoop 上
channelUnregisteredChannel 创建后未注册或者从 EventLoop 取消注册
channelActiveChannel 处于就绪状态,可以被读写
channelInactiveChannel 处于非就绪状态
channelReadChannel 可以从远端读取到数据
channelReadCompleteChannel 读取数据完成

BootStrap 和 ServerBootStrap 分别负责客户端和服务端的启动,它们是非常强大的辅助工具类;

Channel 是网络通信的载体,提供了与底层 Socket 交互的能力

1.2 事件调度层

1.2.1 事件调度层概述

事件调度层的职责是通过 Reactor 线程模型对各类事件进行聚合处理,通过 Selector 主循环线程集成多种事件( I/O 事件、信号事件、定时事件等),实际的业务处理逻辑是交由服务编排层中相关的 Handler 完成。

事件调度层的核心组件包括

  • EventLoopGroup
  • EventLoop

EventLoopGroup 本质是一个线程池,主要负责接收 I/O 请求,并分配线程执行处理请求.

在这里插入图片描述

  • 一个 EventLoopGroup 往往包含一个或者多个 EventLoop。EventLoop 用于处理 Channel 生命周期内的所有 I/O 事件,如 accept、connect、read、write 等 I/O 事件
    • EventLoopGroup可以包含一个或者多个EventLoop
    • 负责处理Channel生命周期内所有的I/O事件
  • EventLoop 同一时间会与一个线程绑定,每个 EventLoop 负责处理多个 Channel
  • 每新建一个 ChannelEventLoopGroup 会选择一个 EventLoop 与其绑定
    • 该 Channel 在生命周期内都可以对 EventLoop 进行多次绑定和解绑。

在这里插入图片描述

EventLoopGroup 的实现类是 NioEventLoopGroup,NioEventLoopGroup 也是 Netty 中最被推荐使用的线程模型

NioEventLoopGroup 继承于 MultithreadEventLoopGroup,是基于 NIO 模型开发的,可以把 NioEventLoopGroup 理解为一个线程池,每个线程负责处理多个 Channel,而同一个 Channel 只会对应一个线程。

EventLoopGroup 是 Netty 的核心处理引擎,其实 EventLoopGroup 是 Netty Reactor 线程模型的具体实现方式,Netty 通过创建不同的 EventLoopGroup 参数配置,就可以支持 Reactor 的三种线程模型

  • 单线程模型
    • EventLoopGroup 只包含一个 EventLoop,Boss 和 Worker 使用同一个EventLoopGroup;
  • 多线程模型
    • EventLoopGroup 包含多个 EventLoop,Boss 和 Worker 使用同一个EventLoopGroup
  • 主从多线程模型
    • EventLoopGroup 包含多个 EventLoop,Boss 是主 ReactorWorker 是从 Reactor
    • 它们分别使用不同的 EventLoopGroup,主 Reactor 负责新的网络连接 Channel 创建,然后把 Channel 注册到从 Reactor。

事件调度层负责监听网络连接和读写操作,然后触发各种类型的网络事件,需要一种机制管理这些错综复杂的事件;

1.2.2 EventLoop【事件循环】

EventLoop 是 Netty 的核心抽象,它代表了一个持续运行的、处理所有分配给它的 I/O 事件和任务的线程。你可以将其理解为一个忠诚的管家,它一生只做一件事:在一个无限循环中,不断地检查自己负责的“领地”(Channel)上是否有事情需要处理(I/O 事件),或者主人(用户代码)是否吩咐了新的任务。

它的核心职责可以简化为一个公式:

  • EventLoop = Thread + Selector + Task Queue

EventLoop的分配策略:

当 Channel 注册到 EventLoopGroup 时,EventLoopGroup 会选择一个 EventLoop 来处理该 Channel 的所有事件:

客户端1
EventLoopGroup
客户端2
客户端3
客户端4
客户端5
EventLoop 1
EventLoop 2
EventLoop 3
Channel 1
Channel 4
Channel 2
Channel 5
Channel 3

默认的分配策略是轮询(Round-Robin),保证 EventLoop 之间的负载均衡。

1.2.3 EventLoopGroup【事件循环组】

EventLoopGroup初始化过程:

  1. 创建线程池:根据指定的线程数创建线程池
  2. 初始化 EventLoop:为每个线程创建对应的 EventLoop
  3. 启动线程:启动所有线程,每个线程运行 EventLoop 的 run () 方法
// 简化的EventLoopGroup初始化逻辑
public NioEventLoopGroup(int nThreads) {this(nThreads, (Executor) null);
}public NioEventLoopGroup(int nThreads, Executor executor) {this(nThreads, executor, SelectorProvider.provider());
}public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {super(nThreads, executor, selectorProvider);
}// 父类MultithreadEventLoopGroup的初始化
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}// 父类MultithreadEventExecutorGroup的初始化
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {// 如果未指定线程数,使用默认值:CPU核心数 * 2if (nThreads <= 0) {throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));}// 如果未指定执行器,创建默认执行器if (executor == null) {executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());}// 创建EventLoop数组children = new EventExecutor[nThreads];// 初始化每个EventLoopfor (int i = 0; i < nThreads; i ++) {boolean success = false;try {// 创建EventLoopchildren[i] = newChild(executor, args);success = true;} catch (Exception e) {// 处理异常} finally {// 处理初始化失败的情况}}// 创建EventLoop选择器chooser = new GenericEventExecutorChooser(children);// 添加终止监听器terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);terminationFuture.addListener(new FutureListener<Object>() {@Overridepublic void operationComplete(Future<Object> future) throws Exception {for (EventExecutor e : children) {e.shutdownGracefully();}}});
}

EventLoopGroup 包含多个 EventLoop,它的主要作用是分配 EventLoop 给新创建的 Channel。它本质是一个 EventLoop 的池子,遵循一种特定的分配策略(通常是轮询 round-robin)。

EventLoopGroup
EventLoop-1
EventLoop-2
EventLoop-N
Channel-1
Channel-2
Channel-3
Channel-4
  • 一个 EventLoopGroup包含多个 EventLoop
  • 每个 EventLoop绑定一个线程,永不更改
  • 一个 EventLoop可以注册多个 Channel,但一个 Channel 只能注册到一个 EventLoop
  • 关键:Channel 的所有 I/O 操作都在同一个线程中执行,线程安全,无需额外同步。
EventLoopGroup
EventLoop 1
EventLoop 2
EventLoop 3
...更多EventLoop
Thread 1
Thread 2
Thread 3
...更多Thread
Selector 1
Selector 2
Selector 3
...更多Selector
  • 每个 EventLoopGroup 包含多个 EventLoop
  • 每个 EventLoop 绑定一个 Thread 和一个 Selector
  • EventLoop 的数量默认等于 CPU 核心数
  • 服务端: 通常有两个 EventLoopGroup
    • BossGroup: 只包含一个或少数几个 EventLoop,专门负责接收客户端的连接请求(OP_ACCEPT 事件),并将建立好的连接“转交”出去。
    • WorkerGroup: 包含多个 EventLoop,负责处理被“转交”过来的连接上的所有后续 I/O 操作(如 OP_READ, OP_WRITE)。
  • 客户端: 只需要一个 EventLoopGroup,负责处理所有连接的事件。
一个 EventLoop 的内部细节
提交异步任务
Single Thread
Selector
Task Queue
Channel-1
Channel-2
Channel-...
Runnable Task A
Runnable Task B
EventLoopGroup 内部结构
EventLoopGroup
EventLoop-1
EventLoop-2
EventLoop-...
EventLoop-N
Client Connection 1
Client Connection 2
Client Connection ...
User Code
  • 一个 EventLoopGroup 包含多个 EventLoop
  • 一个 EventLoop 在它的生命周期内只绑定一个线程Thread),这个线程负责处理所有逻辑。
  • 一个 EventLoop 可以被分配给多个 Channel(即一个线程管理多个连接)。
  • 一个 Channel 在它的生命周期内只注册到一个 EventLoop(即一个连接只由一个线程管理)。这条黄金规则从根本上避免了 Channel 层面的并发问题,无需使用同步锁。
  • 每个 EventLoop 都拥有自己的 SelectorTask Queue

1.2.4 底层实现原理

我们以最常用的 NioEventLoop 为例来分析其工作原理。

NioEventLoop 的工作循环:

等待事件
是否有事件?
处理 I/O 事件
执行任务队列

EventLoop 是一个无限循环,不断执行以下步骤:

  • 轮询(select)I/O 事件(如 Socket 可读、可写)。

  • 处理就绪的 I/O 事件。

  • 执行任务队列中的任务(如 channel.write() 提交的任务)。

  • 执行定时任务(Scheduled Tasks)。

✅ 这就是 Netty 高性能的根源:单线程处理多个 Channel,避免线程上下文切换开销

  • NioEventLoop 的核心是 run() 方法,它是一个永不终止的循环,其工作内容可以精炼为三大步骤:
public final class NioEventLoop extends SingleThreadEventLoop {private final Selector selector;           // JDK NIO Selectorprivate final SelectStrategy selectStrategy; // 选择策略private final Queue<Runnable> taskQueue;   // 普通任务队列private final Queue<ScheduledFutureTask<?>> scheduledTaskQueue; // 定时任务队列// 简化后的代码示例, 只包含核心功能protected void run() {for (;;) { // 无限循环try {// 1. 轮询策略:检查是否有就绪的I/O事件或任务int strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());switch (strategy) {case SelectStrategy.CONTINUE: ... case SelectStrategy.SELECT:// 如果没有任务,就阻塞地等待I/O事件,超时时间为定时任务最早时间strategy = select(timeoutMillis); ... break;default:}// 2. 处理I/O事件:processSelectedKeys()// 3. 处理所有排队的任务:runAllTasks()} catch (Throwable t) {... // 处理异常}}}
}

事件循环主流程

for (;;) {try {// 1. 轮询 I/O 事件int selectedKeys = selector.select(timeoutMillis);// 2. 处理 I/O 事件if (selectedKeys != 0) {processSelectedKeys();}// 3. 执行任务队列runAllTasks();// 4. 执行定时任务runAllTasks(timeoutMillis);} catch (Throwable t) {handleException(t);}
}

关键点

  • selector.select() 是阻塞调用,直到有事件就绪或超时。
  • runAllTasks() 执行用户提交的任务(如 channel.write())。
  • 单线程串行执行,保证了 Channel 的线程安全。
  1. 轮询I/O事件【Select】

    • 智能阻塞: Netty 会检查任务队列是否为空。如果队列为空,它就会调用selector.select(timeout),进入阻塞状态,等待 I/O 事件。

      final class DefaultSelectStrategy implements SelectStrategy {static final SelectStrategy INSTANCE = new DefaultSelectStrategy();private DefaultSelectStrategy() { }@Overridepublic int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {// 如果有任务, 则取出,如果没有,则阻塞return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;}
      }
      
    • 这个 timeout 是基于定时任务队列中最近要执行的任务来计算的,非常智能,既不会错过 I/O 事件,也不会耽误执行定时任务。

      private final IntSupplier selectNowSupplier = new IntSupplier() {@Overridepublic int get() throws Exception {return selectNow();}
      };
      
    • 如果队列非空: 为了避免任务被延迟,它会调用 selector.selectNow(),这是一个非阻塞调用,立刻返回。

      • 优化: Netty 对 selectedKeys 集合进行了优化,使用数组替代了 JDK 的 HashSet,减少了迭代过程中的垃圾产生。
  2. 处理 I/O 事件 (processSelectedKeys)

  • 遍历由 Selector 提供的就绪的 SelectionKey 集合。
  • 根据 key 的兴趣事件(OP_ACCEPT, OP_READ, OP_WRITE等),调用对应的 ChannelPipeline 上的方法。
  • 例如,当有数据可读时(OP_READ),会触发 ChannelRead 事件,数据会从 Pipeline 的头部开始流动,被一个个 ChannelInboundHandler 处理。
  1. 处理异步任务队列 (runAllTasks)
  • 这是 Netty 保证线程安全和高性能的关键。所有用户提交的异步任务(如 ctx.write()channelActive 事件触发后的业务逻辑等)都会被放入这个任务队列。
  • EventLoop 线程会一次性将队列中的所有任务取出来并执行。
  • 这样做的好处是:保证了所有对同一个 Channel 的操作都在同一个线程中顺序执行,完全避免了多线程并发访问的问题,无需同步

线程分配策略

  • 当一个新的 Channel 被创建并注册到 EventLoopGroup 时,EventLoopGroup 会使用 next() 方法选择一个 EventLoop 来管理它。默认策略是轮询(round-robin),即均匀分配,以保证负载均衡。
  1. 任务队列的实现

EventLoop 维护了两个任务队列:

  • 普通任务队列:存放通过 execute ()、submit () 提交的任务
  • 定时任务队列:存放通过 schedule () 等方法提交的定时任务
// 简化的任务处理逻辑
protected boolean runAllTasks() {// 从定时任务队列中移动已到期的任务到普通任务队列fetchFromScheduledTaskQueue();// 获取普通任务队列中的任务Runnable task = pollTask();if (task == null) {return false;}// 处理所有任务for (;;) {try {// 执行任务task.run();} catch (Throwable t) {// 处理任务执行异常}// 获取下一个任务task = pollTask();if (task == null) {// 所有任务处理完毕lastExecutionTime = ScheduledFutureTask.nanoTime();return true;}}
}// 从定时任务队列中获取已到期的任务
private void fetchFromScheduledTaskQueue() {long nanoTime = AbstractScheduledEventExecutor.nanoTime();Runnable scheduledTask = pollScheduledTask(nanoTime);while (scheduledTask != null) {// 将已到期的定时任务添加到普通任务队列taskQueue.add(scheduledTask);scheduledTask = pollScheduledTask(nanoTime);}
}

1.2.5 最佳实践与经验总结

✅ 正确用法(Best Practices)

  1. 线程数设置
    • bossGroup:通常 1 个线程足够。
    • WorkerGroup: 默认值是 CPU核心数 * 2。这是一个很好的经验值。对于纯 I/O 密集型应用(如代理、聊天服务器),可以适当调大。对于计算密集型业务,建议保持默认或甚至更少,并将耗时业务提交到独立的业务线程池,避免阻塞 EventLoop 线程。
  2. EventLoop 复用
    • 一个 Channel 一旦注册到 EventLoop终身绑定,不会迁移。
    • 所有 I/O 操作都在该 EventLoop 线程中执行,无需加锁
  3. 避免阻塞 EventLoop
  • ❌ 不要在 ChannelHandler 中执行耗时操作(如数据库查询、文件读写)。
  • ✅ 应提交到业务线程池:
private final EventExecutorGroup businessGroup = new DefaultEventExecutorGroup(10);
pipeline.addLast(businessGroup, new BusinessHandler()); // 耗时操作放这里
  1. Channel 与 EventLoop 的绑定关系
  • 牢记 “一个 Channel 一生只由一个 EventLoop 处理” 的原则。利用这一点,可以在 ChannelHandler 中使用 ThreadLocal 变量而无需担心线程安全问题。
  1. 优雅关闭
  • 必须调用 shutdownGracefully(),等待正在进行的任务完成。
  • 它会先平滑地关闭所有 Channel,然后不再接收新任务,最后处理完任务队列中的任务后再终止线程。shutdownGracefully() 还支持设置安静期和超时时间。
  1. 监控 EventLoop
  • 可通过 EventLooppendingTasks() 方法监控任务积压情况。

EventLoopEventLoopGroup 是 Netty 的心脏和引擎。它们不仅仅是线程池,更是 Netty 实现高效、有序、线程安全的异步事件处理的核心机制。

  • EventLoopGroup 是工厂和调度器,负责分配 EventLoop
  • EventLoop 是勤劳的工作单元,一人一线程,负责其生命周期内所有的事件和任务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96320.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96320.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/96320.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud 高频面试题详解(含代码示例与深度解析)

文章目录Spring Cloud 高频面试题详解&#xff08;含代码示例与深度解析&#xff09;1. 什么是 Spring Cloud&#xff1f;它与 Spring Boot 有什么关系&#xff1f;2. 服务发现&#xff1a;Eureka 和 Nacos 的区别与选型&#xff1f;Eureka 示例与原理Eureka vs Nacos 对比表3.…

Ascend310B重构驱动run包

在Atlas 200I AI加速模块(Ascend310B)移植过程中如需要将自己编译的Image、dt.img及内核模块打包到启动镜像包中需要对"Ascend-hdk-310b-npu-driver-soc_<version>_linux-aarch64.run"(下面统称驱动run包)进行重构。下面将介绍如何重构run包。 重构驱动run包需…

Leecode hot100 - 287. 寻找重复数

题目描述 287. 寻找重复数 - 力扣&#xff08;LeetCode&#xff09; 定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 &#xff0c;返…

机器人控制器开发(驱动层——奥比大白相机适配)

文章总览 编译OrbbecSDK_ROS2的代码 执行命令 colcon buildros2 launch orbbec_camera dabai.launch.py问题1&#xff1a; 运行时报错&#xff1a; [component_container-1] [ERROR] [1757153916.450795107] [camera.camera_container]: Failed to load library: Could not…

`vcpkg` 微软开源的 C/C++ 包管理工具的使用和安装使用spdlog

vcpkg 是 微软开源的 C/C 包管理工具&#xff0c;类似于 Python 的 pip、Node.js 的 npm、Rust 的 cargo。 它的主要作用是&#xff1a;帮助你快速下载、编译、安装和管理 C/C 第三方库&#xff0c;并自动配置到你的项目&#xff08;比如 Visual Studio、CMake、MSBuild&#x…

Mysql 幻读详解

我们来详细地聊一聊 MySQL InnoDB 中的“幻读”&#xff08;Phantom Read&#xff09;问题。这是一个在数据库事务隔离中非常核心且有时令人困惑的概念。 我会从定义、例子、原因以及解决方案几个方面来彻底讲清楚。 1. 什么是幻读&#xff1f; 官方定义&#xff1a;幻读指的…

如何生成 GitHub Token(用于 Hexo 部署):保姆级教程+避坑指南

如何生成 GitHub Token&#xff08;用于 Hexo 部署&#xff09;&#xff1a;保姆级教程避坑指南 前置说明&#xff1a;为什么需要 GitHub Token&#xff1f; 在使用 Hexo 部署博客到 GitHub Pages 时&#xff0c;你可能会遇到「密码验证失败」或「需要双重验证」的问题——这…

常用加密算法之 AES 简介及应用

相关系列文章 常用加密算法之 SM4 简介及应用常用加密算法之 RSA 简介及应用 引言 AES&#xff08;Advanced Encryption Standard&#xff0c;高级加密标准&#xff09;是一种​​广泛使用的对称分组加密算法​​&#xff0c;它使用相同的密钥进行加密和解密操作&#xff0c…

Java面试问题记录(一)

一、Java 核心基础与进阶1、我们知道 Java 中存在 “值传递” 和 “引用传递” 的说法&#xff0c;你能结合具体例子&#xff0c;说明 Java 到底是值传递还是引用传递吗&#xff1f;这背后涉及到 JVM 中哪些内存区域的交互&#xff1f;Java中只有值传递&#xff0c;不存在引用传…

Redis 主从复制、哨兵与 Cluster 集群部署

文章摘要 本文基于 VMware 虚拟机环境&#xff0c;详细讲解 Redis 高可用架构的核心组件与部署流程&#xff0c;涵盖三大核心模块&#xff1a;Redis 主从复制&#xff08;实现数据备份与读写分离&#xff09;、Redis 哨兵&#xff08;基于主从复制实现故障自动转移&#xff0c;…

ElementUI 中 validateField 对部分表单字段数组进行校验时多次回调问题

目录 方案一&#xff1a;循环调用 Promise.all 合并结果 方案二&#xff1a;直接传入数组字段 总结 在实际业务中&#xff0c;我们有时只需要对表单的部分字段进行校验。ElementUI 提供的 validateField 方法支持单个字段&#xff0c;也支持字段数组&#xff0c;但在使用时…

Visual Studio 2026 震撼发布!AI 智能编程时代正式来临

Visual Studio 2026 震撼发布&#xff01;AI 智能编程时代正式来临 Visual Studio 2026 Insider图标 开发者们的开发环境即将迎来前所未有的智能革命&#xff0c;微软用Visual Studio 2026 重新定义了编码体验。 2025年9月10日&#xff0c;微软正式推出了Visual Studio 2026 In…

Gamma AI:高效制作PPT的智能生成工具

你有没有过这种崩溃时刻&#xff1f;领导让你下午交一份产品介绍 PPT&#xff0c;你打开模板网站翻了半小时没找到合适的&#xff0c;好不容易选了个模板&#xff0c;又得手动调整文字间距、搭配图片&#xff0c;光是把数据做成图表就花了一小时&#xff0c;最后赶出来的 PPT 还…

Python副业新玩法:用Flask搭小程序后端,躺赚被动收入的秘密

凌晨1点&#xff0c;林浩合上电脑时&#xff0c;手机弹出一条微信消息——是上周帮一家社区水果店搭的小程序后端&#xff0c;商家发来了当月的服务费到账提醒。他靠在椅背上笑了&#xff1a;这是这个月第8笔“睡后收入”&#xff0c;加起来刚好覆盖了下个月的房贷。半年前&…

基于PyQt5和阿里云TTS的语音合成应用开发实战[附源码】

项目概述 本文将详细介绍一个基于PyQt5图形界面框架和阿里云TTS(Text-to-Speech)服务的语音合成桌面应用程序的开发过程。该应用提供了完整的文字转语音功能,包括多音色选择、参数调节、实时试听、语速调节和音频下载等特性。 技术栈 前端界面: PyQt5 语音合成: 阿里云TTS服…

基于esp32c3 rust embassy 的墨水屏程序

EPD Reader 基于ESP32-C3的电子墨水屏阅读器&#xff0c;支持ap 配网、sntp 时间同步、txt阅读、天气预报、显示节假日信息、农历显示、自动休眠、web配置等功能。这是在另一个项目 一个rust embassy esp32c3 的练习项目-CSDN博客的基础上修改的 。 界面比较粗糙&#xff0c;以…

Spring 单例测试及线程安全

创建一个账户类 package com.duanhw.demo22.account;import org.springframework.beans.factory.annotation.Value;//Service public class AccountService {Value("1000")private Integer balance;//存款public void deposit(Integer amount){int newbalance balanc…

【vue】组件宽度调整失效后,调整的方法

父容器布局限制 若组件放置在栅格布局&#xff08;如display: grid&#xff09;或弹性容器中&#xff0c;父元素的宽度限制可能导致子组件宽度失效。解决方案是为父容器设置明确的宽度&#xff0c;或通过百分比布局实现自适应16。例如&#xff1a; <div style"width:…

Java 在Word 文档中插入页眉页脚:一份实用的编程指南

在现代企业应用中&#xff0c;Java 开发者经常需要处理各种文档操作&#xff0c;其中对 Word 文档的自动化处理尤为常见。无论是生成报告、合同还是其他商业文档&#xff0c;页眉页脚作为文档结构的重要组成部分&#xff0c;承载着公司 Logo、页码、版权信息等关键内容。手动添…

深入解析Dart虚拟机运行原理

Dart虚拟机运行原理 一、Dart虚拟机 1.1 引言 Dart VM是一种虚拟机&#xff0c;为高级编程语言Dart提供执行环境&#xff0c;但这并意味着Dart在D虚拟机上执行时&#xff0c;总是采用解释执行或者JIT编译。 例如还可以使用Dart虚拟机的AOT管道将Dart代码编译为机器代码&#xf…