Netty 学习记录:一个 CRUD 工程师的踩坑之旅
Netty 学习记录:一个 CRUD 工程师的踩坑之旅
这是一个小白的学习记录
边学边练,把踩过的坑都记下来
先说下我为什么要学 Netty
公司项目要做一个实时通讯系统,领导让我负责技术选型。拿到需求一看,好家伙,要支持数万并发连接,还要低延迟。
我之前也就写过点 Spring Boot 的 CRUD 代码,对网络编程一窍不通。网上的文档要么太深奥,要么太简单。没办法,只能自己一点点啃,顺便把学习笔记整理出来。
如果你也是新手,希望这篇笔记能帮到你。
我的技术水平
- Java 能看懂(CRUD 工程师)
- Linux 命令会用(cd、ls、vim 三件套)
- 网络编程这东西我之前也就停留在"知道 TCP/IP"的 level
坑 1:环境搭建就踩坑
❌ 我当时以为很简单
刚开始学 Netty,我想着不就是加个依赖嘛,So easy!
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
结果启动项目就报错:
Exception in thread "main" java.lang.NoClassDefFoundError: io/netty/util/concurrent/EventExecutorGroup
我当时就懵了。啥?EventExecutorGroup 是个啥?
🔍 问题根源
后来查资料才明白:
- Netty 5.x 版本是 alpha 版本,不稳定
- 很多依赖包版本不兼容
- 网上的教程大多用的是 4.x 版本
好家伙,原来是版本惹的祸。
✅ 解决方案
换成稳定的 4.x 版本:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.87.Final</version>
</dependency>
教训:别用 alpha 版本!老老实实用稳定版。
坑 2:线程模型配置懵逼
❌ 我当时以为很简单
看了教程,说要创建两个 EventLoopGroup,我就随便写了:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
结果运行起来,CPU 使用率直接爆表。
我当时就急了,老板在旁边看着,我满头汗。
🔍 问题根源
后来查资料才明白:
- 不指定线程数时,默认是 CPU 核心数的 2 倍
- bossGroup 只负责接受连接,不需要太多线程
- 我电脑 8 核,一下就创建了 16 个线程
✅ 解决方案
正确配置线程数:
// bossGroup 只需要 1 个线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// workerGroup 可以根据需要调整
EventLoopGroup workerGroup = new NioEventLoopGroup();
教训:线程数不是越多越好,要根据实际需求配置。
坑 3:粘包/拆包问题搞不定
❌ 我当时以为很简单
写了个 Echo 服务器,结果客户端发"Hello",服务器收到"HelloHelloHello",或者收到"Hel"、“lo”。
我当时就懵了,这是啥情况?数据怎么还能粘在一起?
🔍 问题根源
后来查资料才明白:
- TCP 是面向流的协议,数据会被分割或合并
- 这就是粘包/拆包问题
- 需要用解码器来处理
✅ 解决方案
使用 LengthFieldBasedFrameDecoder:
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
1024, // 最大帧长度
0, // 长度字段偏移量
4, // 长度字段长度
0, // 长度调整值
4 // 跳过长度字段
));
ch.pipeline().addLast(new MessageToByteEncoder<String>() {
@Override
protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
byte[] data = msg.getBytes();
out.writeInt(data.length);
out.writeBytes(data);
}
});
教训:网络编程一定要处理粘包/拆包问题!
坑 4:ByteBuf 内存泄漏
❌ 我当时以为很简单
写了个处理器,直接操作 ByteBuf:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf buf = (ByteBuf) msg;
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
String message = new String(data);
System.out.println("Received: " + message);
// 直接返回,没释放 buf
}
结果运行一段时间后,程序崩溃,报 OutOfMemoryError。
🔍 问题根源
后来查资料才明白:
- ByteBuf 需要手动释放
- 特别是直接内存,不释放会导致内存泄漏
- 应该用 SimpleChannelInboundHandler,它会自动释放
✅ 解决方案
使用 SimpleChannelInboundHandler:
public class MyHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println("Received: " + msg);
// 不用手动释放,SimpleChannelInboundHandler 会自动处理
}
}
教训:ByteBuf 一定要记得释放,或者用 SimpleChannelInboundHandler。
验证步骤
1. 编译打包
mvn clean package
2. 启动服务端
java -jar netty-server.jar
3. 启动客户端
java -jar netty-client.jar
4. 测试消息发送
在客户端输入消息,确认服务端能正确接收并回显。
预期结果:消息能正确发送和接收,没有粘包/拆包问题。
总结
我踩过的坑
- 坑 1:用了不稳定的 Netty 5.x 版本 → 换成 4.1.87.Final
- 坑 2:线程数配置不当 → bossGroup 设为 1,workerGroup 默认
- 坑 3:没处理粘包/拆包问题 → 使用 LengthFieldBasedFrameDecoder
- 坑 4:ByteBuf 内存泄漏 → 使用 SimpleChannelInboundHandler
核心要点
- 必须用稳定版本:Netty 4.1.x 是稳定版
- 必须配置线程数:bossGroup 1 个线程足够
- 必须处理粘包/拆包:使用合适的解码器
- 必须注意内存管理:使用 SimpleChannelInboundHandler 或手动释放 ByteBuf
最后说两句
其实 Netty 也没多难,就是刚开始上手的时候有点懵。我也是一点点试,总算是搞定了基础的 Echo 服务器。
肯定有理解不对的地方,欢迎大佬指正。
如果你也遇到类似问题,希望这篇笔记能帮到你。
小技巧:学习 Netty 最好的方法就是跟着官方示例写代码,遇到问题就查文档。别想着一步到位,慢慢来,总能学会的!
更多推荐




所有评论(0)