第 15 / 20 篇

第 14 章 遇到问题时这样排查

14.1 先确认问题发生在哪一层

遵循从外到内的顺序:进程是否启动 → 是否监听正确地址和端口 → 客户端是否连通 → 字节是否到达 → 帧是否完整 → 解码是否正确 → 业务是否执行 → 出站编码与发送是否完成。

每一步都要收集证据。比如“客户端没有输出”可能是没连上,也可能是忘记换行、没有 flush,或响应被错误的 Pipeline 顺序挡住。直接增加线程数通常解决不了这些问题。

14.2 常见现象速查

现象 优先检查 操作方向
Address already in use 端口被占用 停止旧进程或双方改端口
Connection refused 服务未启动或端口不对 看 LISTENING 与客户端参数
连接一直超时 地址、路由、防火墙 先在本机测试,再逐步跨机器
连上但不回复 行分隔符、处理器顺序 确认发送实际换行字节
中文乱码 双方字符集、切帧顺序 统一 UTF-8,先切帧后转字符串
TooLongFrameException 超过协议限制 检查发送内容,合理设置上限
IllegalReferenceCountException 重复释放、释放后使用 逐条跟踪所有权
内存持续上升 泄漏、队列积压、大消息 区分 ByteBuf 泄漏与活跃对象堆积
少量请求很慢 EventLoop 阻塞 检查数据库、sleep、文件 I/O
客户端迟迟不结束 等不到服务器关闭 确认发送 quit 且带换行

14.3 一个“没有响应”的完整推理

假设服务器显示已经监听,客户端也连接成功,但输入 hello 后没有回复。先确认发送的是 hello\n 对应的实际换行,而不是仅仅 hello。行解码器会等待边界,因此没有收到完整帧时不回复是正确行为。

若换行已发送,再检查是否把 StringEncoder 放到了业务处理器之后;若业务使用 ctx.writeAndFlush,该编码器可能没有经过。再检查异常日志,确认不是不支持的出站消息类型。

最后检查对端是否持续读取。写成功、读取成功和业务处理成功要分别观察,不能用某一个日志替代所有层面的证据。

小练习: 当所有连接一起变慢,而 CPU 很低时,为什么仍然要检查 EventLoop 中的阻塞调用?