明天你会感谢今天奋力拼搏的你。
ヾ(o◕∀◕)ノヾ
本篇作为Netty系列的第一篇,主要介绍下Netty的核心组件,让读者对Netty有个初步了解。后续还有两篇,一篇讲Netty的高性能的原因(Reactor线程模型、ByteBuf的内存复用、零拷贝机制等),一篇对Netty的源码进行解读。
Netty是一个Java开源框架,是一个高性能、高可扩展的异步事件驱动的网络应用程序框架,它极大的简化了TCP和UDP客户端和服务器的网络编程开发。
Netty源代码:github地址 | gitee地址 ,本文章基于Netty 4.1.45.Final版本进行介绍
Netty聊天室示例项目:gitee地址
按如上官方的图片可知,netty包含三大块:
1、TCP 是面向连接的可靠传输协议,Netty 提供了针对不同场景的 TCP Channel 实现
2、UDP 是无连接的不可靠传输协议,适用于对实时性要求高的场景(如音视频传输)
3、本地传输相关的 Channel
4、其他特殊 Channel
在 Netty 中,EventLoopGroup 是事件循环的容器,负责管理多个 EventLoop 实例,是实现异步 I/O 操作的核心组件之一。它承担着线程管理、I/O 事件处理调度等关键职责,直接影响 Netty 应用的性能和并发能力。
1、管理 EventLoop 实例
EventLoopGroup 内部维护一个 EventLoop组(每个 EventLoop 通常绑定一个线程),负责为注册的 Channel 分配 EventLoop,并协调它们的工作。
如下图所示,MultithreadEventExecutorGroup是其它EventLoopGroup实现类的父类,用一个数组维护一组EventLoop。
2、调度能力
EventLoopGroup中有两种调度逻辑:一是对EventLoop的调度,还有一种是继承自 ScheduledExecutorService(JUC 中的定时任务接口)兼具事件循环和任务调度能力。
首先说说EventLoop的调度:
MultithreadEventExecutorGroup构造函数中会通过DefaultEventExecutorChooserFactory工厂初始化一个EventLoop选择器,是基于轮询(Round-Robin)策略。
MultithreadEventExecutorGroup中内置了两种选择器实现:
executors[idx.getAndIncrement() & executors.length - 1],可以参考HashMap计算下标逻辑。executors[Math.abs(idx.getAndIncrement() % executors.length)],这种方式的效率没有优化版本好,并且注意:如果idx的值超过整数的最大值会导致数据溢出,此时前后获得的一部分数据是不公平的轮询进行了翻转。如下图EventLoopGroup中会通过next方法,通过选择器选择下一个EventLoop
然后再讨论一下任务调度:
MultithreadEventExecutorGroup继承自AbstractEventExecutorGroup->EventExecutorGroup->ScheduledExecutorService(Java中的定时任务接口),然后在AbstractEventExecutorGroup抽象类中实现了调度的各个方法(下图截取了一部分)。
通过next方法获得EventLoop实例,再通过EventLoop执行任务调度去处理具体的业务逻辑。(EventLoop也继承了EventExecutor -> EventExecutorGroup -> ScheduledExecutorService)
TIP: EventLoop和EventLoopGroup都实现了任务调度,但其设计目的是不一样的,EventLoopGroup是为了实现对EventLoop的负载均衡调度,EventLoop则是为执行业务层面的定时调度。
3、EventLoopGroup在服务端与客户端的角色分工
4、实现类部分介绍
5、线程数量配置
6、注意正确关闭释放资源
EventLoop 是处理 I/O 事件和任务调度的核心组件,是实现异步非阻塞通信的基础。它绑定一个独立线程,通过循环执行事件处理和任务调度,确保同一 Channel 的所有操作在单线程中完成,从而避免线程安全问题并提升性能。
1、核心实现类
Netty 针对不同 I/O 模型和操作系统提供了多种 EventLoop 实现,与 EventLoopGroup 一一对应:
如下图所示,通过MultithreadEventExecutorGroup中定义了抽象方法newChild,EvnetLoopGroup的实现类中会直接new出对应的EventLoop实现来完成一一对应。
Netty4.X的版本采用统一的SingleThreadEventLoop基类,统一EventLoop模型,所有EventLoop实现都继承自它。
SingleThreadEventLoop通过父类SingleThreadEventExecutor实现任务的调度逻辑,然后Bootstrap中会调用EventLoop的execute方法把线程传入来执行异步执行。
ChannelPipeline 是 ChannelHandler 的容器,它采用责任链模式将多个 ChannelHandler 串联成一个处理链,负责接收、传递和处理 Channel 上的所有事件(如连接建立、数据读写、异常等),并支持灵活扩展(添加 / 移除 ChannelHandler)。
DefaultChannelPipeline 的核心特点:
DefaultChannelPipeline是ChannelPipeline的唯一实现类,摘取DefaultChannelPipeline中一部分源码如下图:
入站事件和出站事件的识别:
首先ChannelHandlerMask类中就标明了入站和出站各个事件的标识
// 入站事件 (Inbound Events)
static final int MASK_EXCEPTION_CAUGHT = 1; // 0b00000000000000001
static final int MASK_CHANNEL_REGISTERED = 1 << 1; // 0b00000000000000010
static final int MASK_CHANNEL_UNREGISTERED = 1 << 2; // 0b00000000000000100
static final int MASK_CHANNEL_ACTIVE = 1 << 3; // 0b00000000000001000
static final int MASK_CHANNEL_INACTIVE = 1 << 4; // 0b00000000000010000
static final int MASK_CHANNEL_READ = 1 << 5; // 0b00000000000100000
static final int MASK_CHANNEL_READ_COMPLETE = 1 << 6; // 0b00000000001000000
static final int MASK_USER_EVENT_TRIGGERED = 1 << 7; // 0b00000000010000000
static final int MASK_CHANNEL_WRITABILITY_CHANGED = 1 << 8; // 0b00000000100000000
// 出站事件 (Outbound Events)
static final int MASK_BIND = 1 << 9; // 0b00000001000000000
static final int MASK_CONNECT = 1 << 10; // 0b00000010000000000
static final int MASK_DISCONNECT = 1 << 11; // 0b00000100000000000
static final int MASK_CLOSE = 1 << 12; // 0b00001000000000000
static final int MASK_DEREGISTER = 1 << 13; // 0b00010000000000000
static final int MASK_READ = 1 << 14; // 0b00100000000000000
static final int MASK_WRITE = 1 << 15; // 0b01000000000000000
static final int MASK_FLUSH = 1 << 16; // 0b10000000000000000
然后把这些标识传入对应的入站或出站方法,通过ChannelHandler的executionMask字段(表示该Handler支持哪些事件类型)与运算,结果为0的就说明该Handler不支持该事件类型。
从上图中也能看出来,入站事件的传播方向是从Pipeline的头部向尾部传播,出站事件的传播方向是从Pipeline的尾部向头部传播。
ChannelHandler的executionMask的值是先把所有的掩码都进行或运算一遍,没有此事件再摘除此掩码的值,展示部分逻辑如下:
ChannelHandler 是处理网络事件(如连接建立、数据读写、异常等)的核心组件。它通过 ChannelPipeline 形成责任链,对流经的事件进行加工、转发或终止,是 Netty 灵活性和可扩展性的关键。
1、入站处理器
入站处理器的基类是ChannelInboundHandler接口,核心方法如下:
事件传播方向:从 ChannelPipeline 头部向尾部传播(按 Handler 添加顺序执行)。
2、出站处理器
出站处理器的基类是ChannelInboundHandler接口,核心方法如下:
事件传播方向:从 ChannelPipeline 尾部向头部传播(与添加顺序相反)。
3、适配器类
为避免实现 ChannelInboundHandler 或 ChannelOutboundHandler 的所有方法,Netty 提供了适配器,开发者只需重写需要的方法:
4、事件的传播和终止
全部评论