Netty 核心组件 EventLoop 详解
一、什么是 EventLoop?
EventLoop 可以理解为 Netty 中的"工人",它的核心职责是:
- 处理 I/O 事件:读取数据、写入数据、连接建立等
- 执行普通任务:用户提交的自定义任务
- 执行定时任务:周期性执行的任务
本质
EventLoop = 单线程执行器 + Selector(选择器)
- 每个 EventLoop 内部有一个线程
- 每个 EventLoop 维护一个 Selector,用于监听多个 Channel 的 I/O 事件
- EventLoop 通过
run()方法不断循环处理事件
继承关系
EventLoop
├── 继承 ScheduledExecutorService(Java 线程池)
│ └── 拥有线程池的所有方法(execute、submit、schedule 等)
│
└── 继承 OrderedEventExecutor(Netty 自定义)
├── inEventLoop(Thread thread):判断线程是否属于此 EventLoop
└── parent():查看属于哪个 EventLoopGroup
二、什么是 EventLoopGroup?
EventLoopGroup 是一组 EventLoop 的集合,可以理解为"工人组"。
核心特性
- Channel 绑定:每个 Channel 会绑定到 EventLoopGroup 中的某一个 EventLoop
- 线程安全:同一个 Channel 的所有 I/O 事件都由同一个 EventLoop 处理,保证线程安全
- 负载均衡:多个 Channel 会轮流分配给不同的 EventLoop
继承关系
EventLoopGroup
├── 继承 EventExecutorGroup
│ ├── 实现 Iterable 接口(可以遍历 EventLoop)
│ └── next() 方法:获取下一个 EventLoop
三、EventLoopGroup 基本使用
示例 1:创建 EventLoopGroup
// 创建包含 2 个 EventLoop 的组
DefaultEventLoopGroup group = new DefaultEventLoopGroup(2);
// 获取 EventLoop(轮流获取)
System.out.println(group.next()); // EventLoop-1
System.out.println(group.next()); // EventLoop-2
System.out.println(group.next()); // EventLoop-1(循环)
输出:
io.netty.channel.DefaultEventLoop@60f82f98
io.netty.channel.DefaultEventLoop@35f983a6
io.netty.channel.DefaultEventLoop@60f82f98
示例 2:遍历 EventLoopGroup
DefaultEventLoopGroup group = new DefaultEventLoopGroup(2);
for (EventExecutor eventLoop : group) {
System.out.println(eventLoop);
}
输出:
io.netty.channel.DefaultEventLoop@60f82f98
io.netty.channel.DefaultEventLoop@35f983a6
四、优雅关闭
shutdownGracefully() 方法
group.shutdownGracefully();
执行流程:
- 切换 EventLoopGroup 到关闭状态,拒绝新任务
- 等待任务队列中的任务执行完成
- 停止所有线程
作用: 确保应用在正常有序的状态下退出,避免任务丢失。
五、EventLoop 处理 I/O 事件
场景:服务端使用 2 个 Worker 线程
服务端代码:
new ServerBootstrap()
.group(new NioEventLoopGroup(1), new NioEventLoopGroup(2))
// ↑ Boss Group (1个线程) ↑ Worker Group (2个线程)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf byteBuf = (ByteBuf) msg;
byte[] buf = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(buf);
log.debug(new String(buf));
}
});
}
}).bind(8080).sync();
客户端代码(启动 3 次,分别发送 “zhangsan”、“lisi”、“wangwu”):
Channel channel = new Bootstrap()
.group(new NioEventLoopGroup(1))
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
}
})
.connect("localhost", 8080)
.sync()
.channel();
channel.writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("zhangsan".getBytes()));
Thread.sleep(2000);
channel.writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("zhangsan".getBytes()));
输出结果:
22:03:34 [DEBUG] [nioEventLoopGroup-3-1] - zhangsan
22:03:36 [DEBUG] [nioEventLoopGroup-3-1] - zhangsan
22:05:36 [DEBUG] [nioEventLoopGroup-3-2] - lisi
22:05:38 [DEBUG] [nioEventLoopGroup-3-2] - lisi
22:06:09 [DEBUG] [nioEventLoopGroup-3-1] - wangwu
22:06:11 [DEBUG] [nioEventLoopGroup-3-1] - wangwu
分析
- Worker Group 有 2 个线程:
nioEventLoopGroup-3-1和nioEventLoopGroup-3-2 - Channel 与 EventLoop 绑定:
- 第 1 个客户端 → 绑定到
EventLoop-1 - 第 2 个客户端 → 绑定到
EventLoop-2 - 第 3 个客户端 → 绑定到
EventLoop-1(轮流分配)
- 第 1 个客户端 → 绑定到
- 同一个 Channel 的所有事件都由同一个 EventLoop 处理
图示:
客户端1 (zhangsan) ──→ EventLoop-1 (线程1)
客户端2 (lisi) ──→ EventLoop-2 (线程2)
客户端3 (wangwu) ──→ EventLoop-1 (线程1)
六、分工:NIO EventLoop + 普通 EventLoop
场景:将耗时任务交给普通 EventLoop 处理
为什么需要分工?
- NIO EventLoop 负责处理 I/O 事件(读写数据)
- 如果业务逻辑耗时较长(如数据库查询),会阻塞 I/O 处理
- 解决方案:将耗时任务交给普通 EventLoop 处理
服务端代码:
// 创建 2 个普通工人(非 NIO)
DefaultEventLoopGroup normalWorkers = new DefaultEventLoopGroup(2);
new ServerBootstrap()
.group(new NioEventLoopGroup(1), new NioEventLoopGroup(2))
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
// 第 1 个 handler:由 NIO EventLoop 处理
ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
// 第 2 个 handler:由普通 EventLoop 处理
ch.pipeline().addLast(normalWorkers, "myhandler",
new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf byteBuf = (ByteBuf) msg;
byte[] buf = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(buf);
log.debug(new String(buf));
}
});
}
}).bind(8080).sync();
输出结果:
22:19:48 [DEBUG] [nioEventLoopGroup-4-1] LoggingHandler - READ: 8B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 7a 68 61 6e 67 73 61 6e |zhangsan |
+--------+-------------------------------------------------+----------------+
22:19:48 [DEBUG] [defaultEventLoopGroup-2-1] - zhangsan
22:20:24 [DEBUG] [nioEventLoopGroup-4-2] LoggingHandler - READ: 4B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 6c 69 73 69 |lisi |
+--------+-------------------------------------------------+----------------+
22:20:25 [DEBUG] [defaultEventLoopGroup-2-2] - lisi
22:20:38 [DEBUG] [nioEventLoopGroup-4-1] LoggingHandler - READ: 6B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 77 61 6e 67 77 75 |wangwu |
+--------+-------------------------------------------------+----------------+
22:20:38 [DEBUG] [defaultEventLoopGroup-2-1] - wangwu
分析
- LoggingHandler 由 NIO EventLoop 执行(
nioEventLoopGroup-4-1) - 自定义 Handler 由普通 EventLoop 执行(
defaultEventLoopGroup-2-1) - 实现了分工:I/O 操作和业务逻辑分离
图示:
客户端数据到达
↓
NIO EventLoop (线程1)
↓ 执行 LoggingHandler(记录日志)
↓
普通 EventLoop (线程3)
↓ 执行自定义 Handler(业务逻辑)
七、Handler 执行中如何换人?
核心代码:invokeChannelRead()
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
EventExecutor executor = next.executor();
// 判断下一个 handler 的线程是否与当前线程相同
if (executor.inEventLoop()) {
// 相同:直接调用
next.invokeChannelRead(msg);
} else {
// 不同:将任务提交给下一个 handler 的线程执行(换人)
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(msg);
}
});
}
}
换人机制
- 同一个线程:直接调用下一个 handler
- 不同线程:将任务封装后提交给下一个 handler 的线程
示例:
Handler1 (NIO EventLoop-1)
↓ 检测到 Handler2 属于不同线程
↓ 将任务提交给 普通 EventLoop-3
Handler2 (普通 EventLoop-3) 执行
八、EventLoop 处理普通任务
示例:提交普通任务
NioEventLoopGroup nioWorkers = new NioEventLoopGroup(2);
log.debug("server start...");
Thread.sleep(2000);
// 提交普通任务
nioWorkers.execute(() -> {
log.debug("normal task...");
});
输出:
22:30:36 [DEBUG] [main] - server start...
22:30:38 [DEBUG] [nioEventLoopGroup-2-1] - normal task...
使用场景
- 执行耗时较长的任务(避免阻塞 I/O 线程)
- 数据库查询、文件操作等
九、EventLoop 处理定时任务
示例:提交定时任务
NioEventLoopGroup nioWorkers = new NioEventLoopGroup(2);
log.debug("server start...");
Thread.sleep(2000);
// 提交定时任务(每隔 1 秒执行一次)
nioWorkers.scheduleAtFixedRate(() -> {
log.debug("running...");
}, 0, 1, TimeUnit.SECONDS);
输出:
22:35:15 [DEBUG] [main] - server start...
22:35:17 [DEBUG] [nioEventLoopGroup-2-1] - running...
22:35:18 [DEBUG] [nioEventLoopGroup-2-1] - running...
22:35:19 [DEBUG] [nioEventLoopGroup-2-1] - running...
22:35:20 [DEBUG] [nioEventLoopGroup-2-1] - running...
...
使用场景
- 心跳检测
- 定时清理缓存
- 定时统计数据
十、总结
EventLoop 核心概念
| 概念 | 说明 |
|---|---|
| EventLoop | 单线程执行器 + Selector,处理 I/O 事件、普通任务、定时任务 |
| EventLoopGroup | EventLoop 的集合,负责管理多个 EventLoop |
| Channel 绑定 | 每个 Channel 绑定到一个 EventLoop,保证线程安全 |
| 分工机制 | NIO EventLoop 处理 I/O,普通 EventLoop 处理业务逻辑 |
| 换人机制 | 不同线程的 handler 通过任务提交实现切换 |
EventLoop 的三大能力
- 处理 I/O 事件:读写数据、连接管理
- 执行普通任务:用户自定义任务
- 执行定时任务:周期性任务
最佳实践
- Boss Group 使用 1 个线程:只负责接收连接
- Worker Group 使用多个线程:处理多个客户端的 I/O
- 耗时任务使用独立的 EventLoopGroup:避免阻塞 I/O 线程
- 优雅关闭:使用
shutdownGracefully()确保任务完成
十一、图解总结
单 EventLoopGroup 模式
ServerBootstrap
↓
EventLoopGroup (2个线程)
├── EventLoop-1 → Channel-1, Channel-3
└── EventLoop-2 → Channel-2
双 EventLoopGroup 模式(推荐)
ServerBootstrap
├── Boss Group (1个线程)
│ └── EventLoop-Boss → 监听端口,接收连接
│
└── Worker Group (2个线程)
├── EventLoop-1 → Channel-1 (I/O 处理)
└── EventLoop-2 → Channel-2 (I/O 处理)
三 EventLoopGroup 模式(分工)
ServerBootstrap
├── Boss Group (1个线程)
│ └── EventLoop-Boss → 监听端口
│
├── Worker Group (2个线程,NIO)
│ ├── EventLoop-1 → Channel-1 (I/O 处理)
│ └── EventLoop-2 → Channel-2 (I/O 处理)
│
└── Business Group (2个线程,普通)
├── EventLoop-3 → 业务逻辑处理
└── EventLoop-4 → 业务逻辑处理
18



被折叠的 条评论
为什么被折叠?



