当前位置: 首页 > news >正文

Netty从0到1系列之EventLoop

文章目录

  • 一、EventLoop
    • 1.1 EventLoop 是什么?为什么需要它?
      • 1.1.1 核心概念
      • 1.1.2 要解决的问题:传统的并发模型缺陷
    • 1.2 EventLoop核心架构与工作原理
    • 1.3 EventLoop 与 EventLoopGroup 的关系
    • 1.4 EventLoop的继承体系
    • 1.5 EventLoop的核心工作: 任务调度
      • 1.5.1 立即执行异步任务 (Runnable)
      • 1.5.2 定时任务: 延迟执行
      • 1.5.3 固定速率执行任务
      • 1.5.4 固定延迟定时任务
      • 1.5.5 EventLoop基础使用
      • 1.5.6 EventLoop与Channel绑定
    • 1.6 EventLoop最佳实践
    • 1.7 EventLoop优缺点总结
    • 1.8 EventLoop核心价值
    • 1.9 一句话总结


推荐阅读:

【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系列之整体架构、入门程序


一、EventLoop

1.1 EventLoop 是什么?为什么需要它?

1.1.1 核心概念

EventLoop 是 Netty 的核心执行单元。它的名字完美地描述了它的工作:

  • Event:它负责处理各种 I/O 事件(如数据可读、连接就绪、用户任务)。
  • Loop:它在一个无限循环中运行,不断地等待事件、处理事件。

你可以将它理解为一个专为处理 Channel I/O 和任务而优化的、加强版的单线程执行器

Netty 架构
EventLoopGroup
EventLoop-1
EventLoop-2
EventLoop-N
Channel-1
Channel-2
Channel-3
Channel-4

🌟 核心职责

  • I/O 事件轮询select
  • I/O 事件处理read/write/accept
  • 任务执行Runnable 任务队列)
  • 定时任务调度
  • 保证线程安全(无锁串行化)

1.1.2 要解决的问题:传统的并发模型缺陷

在传统的“一个连接,一个线程”(BIO)模型中,当连接数暴涨时,线程数量也随之暴涨,导致:

  • 巨大的内存消耗:每个线程都需要独立的栈内存(通常1MB左右)。
  • 巨大的 CPU 开销:线程上下文切换会消耗大量 CPU 资源。
  • 系统资源耗尽:最终系统无法创建新线程,性能急剧下降。

EventLoop 的解决方案:

  • 使用少量线程(通常为核心数的两倍)来管理海量连接
  • 每个 EventLoop 绑定一个线程,负责处理多个 Channel 上的所有事件。
  • 这实现了高效的资源利用和无锁化的串行设计,从根本上解决了传统模型的缺陷。

1.2 EventLoop核心架构与工作原理

EventLoop 的核心工作机制可以概括为一个高效的任务处理循环。其工作流程的精妙之处在于它如何平衡 I/O 事件和异步任务的执行,下图清晰地展示了这一过程:

Yes
No
EventLoop Thread Start
进入循环
检查是否有待处理任务?
处理所有异步任务
runAllTasks
Select 轮询 I/O 事件
selector.select
处理就绪的 I/O 事件
processSelectedKeys
再次检查任务队列
处理I/O事件
processSelectedKeys

这个循环是 Netty 高性能的基石,它确保了:

  1. I/O 高响应性:优先处理就绪的 I/O 事件,保证网络通信的低延迟。
  2. 任务公平性:在 I/O 事件的间隙处理异步任务,防止任务饿死。
  3. 资源高效利用:在没有任务和 I/O 事件时,线程会优雅地阻塞在 select() 上,避免空转消耗 CPU。

1.3 EventLoop 与 EventLoopGroup 的关系

绑定到 EventLoop 2 的 Channels
绑定到 EventLoop 1 的 Channels
EventLoopGroup (线程池)
Channel C
Channel A
Channel B
EventLoop 1
EventLoop 2
EventLoop 3
...
单一专属线程 Thread-1
单一专属线程 Thread-2
单一专属线程 Thread-3
  • EventLoopGroup:包含多个 EventLoop 的池子。Netty 通常创建两个 group:

    • BossGroup:负责接受新连接。连接接受后,它将 Channel 注册给 WorkerGroup 中的一个 EventLoop

    • WorkerGroup:负责处理已接受连接的 I/O 读写

  • EventLoop一个 EventLoop 在其生命周期内只绑定一个线程。反之,该线程也只服务于这个 EventLoop。

  • Channel一个 Channel 在其生命周期内只注册到一个 EventLoop。反之,一个 EventLoop 可以被注册给多个 Channel

这条规则是 Netty 实现无锁化和线程安全架构的基石! 它保证了对同一个 Channel 的所有操作始终由同一个线程串行执行,彻底避免了复杂的同步。

1.4 EventLoop的继承体系

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

io.netty.util.concurrent-> SingleThreadEventExecutor-> SingleThreadEventLoop-> NioEventLoop / EpollEventLoop / ...
  • SingleThreadEventExecutor:封装了单一线程任务队列的核心逻辑。
  • SingleThreadEventLoop:增加了注册 Channel 和执行 I/O 操作的能力。
  • NioEventLoop:基于 Java NIO Selector 的具体实现,也是最常用的实现。

ScheduledExecutorService

public interface ScheduledExecutorService extends ExecutorService {// other code ... 
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OrderedEventExecutor

public interface OrderedEventExecutor extends EventExecutor {
}

EventExecutor

public interface EventExecutor extends EventExecutorGroup {@OverrideEventExecutor next();EventExecutorGroup parent(); // ✅ parent方法来看看自己属于哪个EventLoopGroupboolean inEventLoop();boolean inEventLoop(Thread thread); // ✅ 判断一个线程是否属于当前的EventLoop<V> Promise<V> newPromise();<V> ProgressivePromise<V> newProgressivePromise();<V> Future<V> newSucceededFuture(V result);<V> Future<V> newFailedFuture(Throwable cause);
}

[!note]

🥭总结

  • EventLoop继承自JUC包下的ScheduledExecutorService, 因此包含了线程池中所有的方法.
  • EventLoop继承自Netty自己的OrderedEventExecutor
    • 提供了 boolean inEventLoop(Thread thread) 方法,判断一个线程是否属于此 EventLoop
    • 提供了 parent 方法来看看自己属于哪个 EventLoopGroup

1.5 EventLoop的核心工作: 任务调度

EventLoop 继承自 ScheduledExecutorService,因此它具备 JDK 线程池的所有能力,并且更加强大。

Channel channel = ...;
EventLoop eventLoop = channel.eventLoop();

1.5.1 立即执行异步任务 (Runnable)

// 1. 立即执行异步任务 (Runnable)
eventLoop.execute(new Runnable() {@Overridepublic void run() {System.out.println("This is executed asynchronously in the EventLoop thread: "+ Thread.currentThread().getName());// 这里可以安全地操作与这个EventLoop关联的Channelchannel.writeAndFlush("Data from task");}
});

1.5.2 定时任务: 延迟执行

// 2. 定时任务:延迟执行
ScheduledFuture<?> future = eventLoop.schedule(new Runnable() {@Overridepublic void run() {System.out.println("Executed after 5 seconds delay");}
}, 5, TimeUnit.SECONDS); // 延迟5秒

1.5.3 固定速率执行任务

// 3. 固定速率定时任务(忽略任务执行时间)
eventLoop.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("Executed every 3 seconds");}
}, 1, 3, TimeUnit.SECONDS); // 初始延迟1秒,之后每3秒一次

1.5.4 固定延迟定时任务

// 4. 固定延迟定时任务(等待任务执行完成后,再延迟)
eventLoop.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000); // 模拟耗时任务} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Executed with 3 seconds delay after the previous task finished");}
}, 1, 3, TimeUnit.SECONDS);

关键点:

  • 所有通过 executeschedule 提交的任务,都会被放入该 EventLoop 的任务队列中。
  • EventLoop 线程会在其运行循环中消费并执行这些任务。
  • 因为这些任务和在同一个 EventLoop 上处理的 I/O 事件是串行执行的,所以它们是线程安全的。

1.5.5 EventLoop基础使用

package cn.tcmeta.demo02;import io.netty.channel.EventLoop;
import io.netty.channel.nio.NioEventLoopGroup;import java.util.concurrent.TimeUnit;/*** @author: laoren* @description: EventLoop的基本使用* @version: 1.0.0*/
public class EventLoopExample {public static void main(String[] args) {// 创建一个NioEventLoopGroup(包含多个 EventLoop)NioEventLoopGroup group = new NioEventLoopGroup(2);try {// 2. 获取一个EventLoopEventLoop eventLoop = group.next();System.out.printf("线程名称: 【%s】 , -------  %s \n", Thread.currentThread().getName(), "");// 3. 提交一个普通任务eventLoop.execute(() -> {System.out.printf("线程名称: 【%s】 , ----------: %s \n", Thread.currentThread().getName(), "✅");});// 4. 提交定时任务eventLoop.schedule(() -> {System.out.printf("线程名称: 【%s】 , ----------: %s \n", Thread.currentThread().getName(), "🥭");}, 2, TimeUnit.SECONDS);// 5. 提交一个周期性任务eventLoop.scheduleAtFixedRate(() -> {System.out.printf("线程名称: 【%s】 , ----------: %s \n", Thread.currentThread().getName(), "🎁");}, 0, 1, TimeUnit.SECONDS);try {TimeUnit.MILLISECONDS.sleep(5000);}catch (InterruptedException e){e.printStackTrace();}}catch (Exception e){e.printStackTrace();}finally {group.shutdownGracefully(); // 关闭线程池}}
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.5.6 EventLoop与Channel绑定

package cn.tcmeta.demo02;import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoop;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;/*** @author: laoren* @date: 2025/9/1 14:19* @description: Channel与EventLoop绑定* @version: 1.0.0*/
public class ChannelEventLoopBinding {public static void main(String[] args) {NioEventLoopGroup boosGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup(2);try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(boosGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {EventLoop eventLoop = ch.eventLoop();System.out.println("🔗 Channel " + ch.id() +" bound to EventLoop thread: " +Thread.currentThread().getName());// 在 EventLoop 线程中执行任务eventLoop.execute(() -> {System.out.println("⚡ Channel " + ch.id() +" executing task in " +Thread.currentThread().getName());// 模拟响应ch.writeAndFlush(Unpooled.copiedBuffer("Hello from " + Thread.currentThread().getName() + "\n",java.nio.charset.StandardCharsets.UTF_8));});}});ChannelFuture future = bootstrap.bind(8080).sync();System.out.println("🚀 Server started at port 8080");future.channel().closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {boosGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.6 EventLoop最佳实践

✅ 推荐实践

实践说明
合理设置线程数EventLoopGroup 线程数 = CPU 核心数 × 2
避免阻塞 EventLoop不要在 ChannelHandler 中执行 Thread.sleep()、数据库查询等耗时操作
使用独立业务线程池耗时任务提交到业务线程池
使用 eventLoop().execute()确保代码在 I/O 线程执行
优雅关闭调用 shutdownGracefully()

⚠️ 常见错误

// ❌ 错误:阻塞 I/O 线程
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {Thread.sleep(5000); // 严重阻塞!ctx.writeAndFlush(msg);
}// ✅ 正确:提交到业务线程池
private final ExecutorService businessPool = Executors.newFixedThreadPool(10);@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {businessPool.execute(() -> {// 耗时业务processBusiness(msg);ctx.writeAndFlush(result);});
}

1.7 EventLoop优缺点总结

✅ 优点

优点说明
高性能无锁串行化,避免锁竞争
高并发单线程处理多连接,支持百万级并发
线程安全同一 Channel 的操作由同一线程执行
资源高效线程数可控,减少上下文切换
任务统一调度I/O、任务、定时任务统一处理

❌ 缺点

缺点说明
调试困难异步编程,堆栈不直观
阻塞风险一旦 I/O 线程阻塞,整个 Channel 挂起
学习成本高需理解事件驱动、Reactor 模式
内存管理复杂ByteBuf 需手动释放

1.8 EventLoop核心价值

维度说明
核心思想一个线程一个事件循环,串行化处理
关键技术Reactor 模式、无锁设计、任务队列
性能优势低延迟、高吞吐、高并发
设计精髓“让 I/O 线程只做 I/O 事”
适用场景所有 Netty 网络应用的基础

1.9 一句话总结

💡 一句话总结

EventLoop 是 Netty 实现高性能网络通信的“心脏” —— 它通过 无锁串行化统一事件循环,将复杂的并发问题转化为简单的串行处理,是现代异步网络框架的典范设计。


文章转载自:

http://5zghv63g.qLjxm.cn
http://hij8kyXP.qLjxm.cn
http://mfHw42Jz.qLjxm.cn
http://fTR0xyrd.qLjxm.cn
http://mhDBNHfM.qLjxm.cn
http://TZGDYWKA.qLjxm.cn
http://Mmqjbsol.qLjxm.cn
http://qOgKGiSQ.qLjxm.cn
http://TByUZXek.qLjxm.cn
http://mkBD9H5o.qLjxm.cn
http://1cFw940S.qLjxm.cn
http://0muqw1MM.qLjxm.cn
http://J0uPpaSr.qLjxm.cn
http://0kiKioaA.qLjxm.cn
http://I6co5T6a.qLjxm.cn
http://BWJkhn4f.qLjxm.cn
http://kp4v0esh.qLjxm.cn
http://Y4yhshHf.qLjxm.cn
http://PEc2Ot5g.qLjxm.cn
http://R0YyoPKD.qLjxm.cn
http://b7Pm3k3o.qLjxm.cn
http://IK3dw2OA.qLjxm.cn
http://Zd7zKLJr.qLjxm.cn
http://rGvGosri.qLjxm.cn
http://BxqGu2c7.qLjxm.cn
http://5dH4QJbq.qLjxm.cn
http://F3UjWWFy.qLjxm.cn
http://k1jw7tq2.qLjxm.cn
http://vo6JhFdi.qLjxm.cn
http://2OUlFo4V.qLjxm.cn
http://www.dtcms.com/a/370568.html

相关文章:

  • 在Ubuntu上配置Nginx实现开机自启功能
  • 智慧灌区系统:科技赋能,让农田灌溉更智能、更高效、更可持续
  • 第2课:环境搭建:基于DeepSeek API的开发环境配置
  • 本地MOCK
  • 使用Docker安装Stirling-PDF(PDF工具)
  • 交换机详细
  • 综合安防集成系统解决方案,智慧园区,智慧小区安防方案(300页Word方案)
  • Spring Data JPA 对PostgreSQL向量数据的支持
  • 去中心化投票系统开发教程 第二章:开发环境搭建
  • BOSS直聘招聘端自动化识别策略调整(20250905)
  • MySQL--索引和事务
  • c++之基础B(双重循环)(第五课)
  • 3、工厂模式
  • 2025高教社国赛数学建模竞赛B题完整参考论文(含模型和代码)
  • MCP Token超限问题解决方案
  • 并行编程实战——CUDA编程的纹理内存
  • 京东商品评论API开发指南
  • Day27 函数2 装饰器
  • YOLOv8支持旋转框检测(OBB)任务随记
  • 解决VMWare网络适配器的桥接模式 ping 重复数据包DUP问题
  • Elasticsearch优化从入门到精通
  • 【开题答辩全过程】以电商数据可视化系统为例,包含答辩的问题和答案
  • 大模型热潮中的“连接器”:深入解析模型上下文协议 (MCP)
  • Java学习笔记二(类)
  • NPU边缘推理识物系统
  • 避免使用非const全局变量:C++中的最佳实践 (C++ Core Guidelines)
  • 贪心算法应用:保险理赔调度问题详解
  • ERP系统价格一般要多少?ERP定制开发性价比高,功能模块自由选配
  • 接口权限验证有哪些方式
  • 【数据分享】土地利用shp数据分享-广东