读 Netty 源码的目标不是记住所有类,而是能回答:一个配置如何变成 Channel,一次就绪事件如何进入 Pipeline,一次写操作何时真正到达 Socket。 最有效的方法是带着可运行断点追一条主线。
Table of contents
Open Table of contents
准备最小调试入口
先运行Netty 编解码样例下载,确认协议层测试是绿色的。 源码调试再换成最小 Echo 服务器,固定 Netty 版本并下载对应 source JAR。不要一开始调试完整业务应用。
启动主线:配置如何落地
从 ServerBootstrap.bind() 开始,按以下问题记录调用链:
- 哪个
ChannelFactory创建监听 Channel; - Channel 何时注册到 EventLoop;
option和 Handler 何时应用;- 底层 bind 何时执行;
ChannelFuture在哪里标记成功或失败。
异步 API 常见模式是:调用方拿到 Future,实际操作被提交到 EventLoop。断点看到“当前方法已经返回”不代表绑定或写入完成。
接受与读取主线
监听 Channel 收到接受事件后创建子 Channel,初始化它的 Pipeline,再注册到 worker EventLoop。子 Channel 读到字节时,底层 unsafe 层推进读循环,
分配 ByteBuf 并触发入站事件。
沿 fireChannelRead 查看 ChannelHandlerContext 如何寻找下一个入站 Handler。重点记录消息类型、引用计数和线程名,而不是复制几十层栈帧。
写入主线
write 把消息沿出站方向编码并加入出站缓冲区,flush 才推动已写消息交给底层传输。Socket 暂时不可写时,剩余数据必须保留,等下一次写就绪继续。
因此只调用 write 忘记 flush,或无界 writeAndFlush,分别会造成消息滞留和写队列膨胀。
观察 Promise 在成功写入、连接关闭和异常时如何完成。业务不能把“提交写操作”误当成“客户端已收到”。
一张可复查的阅读记录
每次源码阅读只输出四项:入口场景、关键对象、调用主线、验证断点。再补一个“如果这里变慢,会看到什么指标”的问题。
场景:子连接第一次读
入口:就绪事件
状态:Channel -> EventLoop -> Pipeline -> ByteBuf
验证:线程名固定;Handler 顺序与配置一致;消息最终释放
Netty 官方文档提供 4.2 的 API 与源码入口。阅读时版本必须与项目依赖一致,因为内部类名和实现细节会变化。
下一步
阅读手写轻量 RPC 框架,用协议、关联 ID、截止时间和服务调度组合一个完整调用链。