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,我就随便写了:
EventLoopGroupbossGroup=newNioEventLoopGroup();EventLoopGroupworkerGroup=newNioEventLoopGroup();结果运行起来,CPU 使用率直接爆表。
我当时就急了,老板在旁边看着,我满头汗。
🔍 问题根源
后来查资料才明白:
- 不指定线程数时,默认是 CPU 核心数的 2 倍
- bossGroup 只负责接受连接,不需要太多线程
- 我电脑 8 核,一下就创建了 16 个线程
✅ 解决方案
正确配置线程数:
// bossGroup 只需要 1 个线程EventLoopGroupbossGroup=newNioEventLoopGroup(1);// workerGroup 可以根据需要调整EventLoopGroupworkerGroup=newNioEventLoopGroup();教训:线程数不是越多越好,要根据实际需求配置。
坑 3:粘包/拆包问题搞不定
❌ 我当时以为很简单
写了个 Echo 服务器,结果客户端发"Hello",服务器收到"HelloHelloHello",或者收到"Hel"、“lo”。
我当时就懵了,这是啥情况?数据怎么还能粘在一起?
🔍 问题根源
后来查资料才明白:
- TCP 是面向流的协议,数据会被分割或合并
- 这就是粘包/拆包问题
- 需要用解码器来处理
✅ 解决方案
使用 LengthFieldBasedFrameDecoder:
ch.pipeline().addLast(newLengthFieldBasedFrameDecoder(1024,// 最大帧长度0,// 长度字段偏移量4,// 长度字段长度0,// 长度调整值4// 跳过长度字段));ch.pipeline().addLast(newMessageToByteEncoder<String>(){@Overrideprotectedvoidencode(ChannelHandlerContextctx,Stringmsg,ByteBufout)throwsException{byte[]data=msg.getBytes();out.writeInt(data.length);out.writeBytes(data);}});教训:网络编程一定要处理粘包/拆包问题!
坑 4:ByteBuf 内存泄漏
❌ 我当时以为很简单
写了个处理器,直接操作 ByteBuf:
@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg){ByteBufbuf=(ByteBuf)msg;byte[]data=newbyte[buf.readableBytes()];buf.readBytes(data);Stringmessage=newString(data);System.out.println("Received: "+message);// 直接返回,没释放 buf}结果运行一段时间后,程序崩溃,报 OutOfMemoryError。
🔍 问题根源
后来查资料才明白:
- ByteBuf 需要手动释放
- 特别是直接内存,不释放会导致内存泄漏
- 应该用 SimpleChannelInboundHandler,它会自动释放
✅ 解决方案
使用 SimpleChannelInboundHandler:
publicclassMyHandlerextendsSimpleChannelInboundHandler<String>{@OverrideprotectedvoidchannelRead0(ChannelHandlerContextctx,Stringmsg){System.out.println("Received: "+msg);// 不用手动释放,SimpleChannelInboundHandler 会自动处理}}教训:ByteBuf 一定要记得释放,或者用 SimpleChannelInboundHandler。
验证步骤
1. 编译打包
mvn clean package2. 启动服务端
java-jar netty-server.jar3. 启动客户端
java-jar netty-client.jar4. 测试消息发送
在客户端输入消息,确认服务端能正确接收并回显。
预期结果:消息能正确发送和接收,没有粘包/拆包问题。
总结
我踩过的坑
- 坑 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 最好的方法就是跟着官方示例写代码,遇到问题就查文档。别想着一步到位,慢慢来,总能学会的!