Netty
配套知识点:Netty、Java 网络编程与 NIO
怎么用这份笔记
- 分级:
★★★ 必须吃透/★★☆ 了解即可(记结论就行)/★☆☆ 可以跳过(一句话看一眼)- 【简历亮点,高频追问】:简历里写过的技术,面试官会顺着往下挖,这段必须能展开讲
- 每个考点按「定义 → 原理 → 要点 / 易错点」组织,只讲知识点
1. Netty 是什么、其设计哲学 ★★★
Netty 是一个基于 Java NIO 的异步事件驱动的高性能网络应用框架,用来快速开发高性能、高可靠的网络服务端和客户端。
- 它把 NIO 里难用又容易出错的底层细节(Selector、ByteBuffer、Channel)全部封装掉,对外只暴露统一的 API。
- 核心抽象是「Channel + Pipeline + Handler」这套组件化模型:连接由 Channel 表示,处理逻辑挂在 Pipeline 的 Handler 链上,网络编程可以像搭积木一样组合。
- 运行机制是非阻塞 IO + Reactor 线程模型,把连接管理和业务处理解耦。
- 设计哲学:异步非阻塞、事件驱动、组件可插拔(Pipeline、Handler、Codec 都能自定义替换)、追求高性能(零拷贝、内存池、无锁化串行设计)。
- 最对口的场景是高并发长连接:设备接入层几千个长连接,只靠少量 worker 线程就能撑住。
2. Netty 相比传统 Java 网络编程(BIO/NIO)的优势 ★★★
三种 IO 模型的差别:
- BIO:同步阻塞,一个连接一个线程,线程阻塞在
read上等数据。连接一多线程数爆炸,CPU 大量时间耗在线程切换上,还要为每个线程付出线程栈内存。 - NIO:同步非阻塞,用 Selector 多路复用,一个线程管多个连接。但原生 API 太底层、难用:ByteBuffer 读写要
flip切换模式、粘包半包要自己处理,还有 epoll 空轮询 bug(Selector 空转导致 CPU 被打满),代码又臭又长。 - AIO:异步非阻塞,但生态不成熟、实际用得少。
Netty 把这些坑都填了,还额外做了性能优化:
- 提供统一的异步事件驱动 API,屏蔽底层细节;
- 自带编解码器、粘包拆包处理器、空闲检测等开箱即用的 Handler,不用自己造轮子;
- 额外的性能优化:零拷贝、内存池、Reactor 线程模型。
一句话:BIO 连接多了扛不住,NIO 太底层难写,Netty 是 NIO 的高层封装加优化。
3. Netty 的 Reactor 模型及单线程/多线程/主从多线程三种模式 ★★★
Reactor 是一种基于 IO 多路复用的事件分发模型:一个(或一组)线程负责监听所有连接的 IO 事件,事件到达后就分发给对应的 handler 处理。
三种模式:
- 单 Reactor 单线程:一个线程既 accept 连接,又读写数据,还处理业务。全程串行,一个 handler 处理慢就卡住所有连接,只适合小并发场景。
- 单 Reactor 多线程:一个 Reactor 线程只负责 accept 和事件分发,业务处理丢进线程池,IO 线程不再被业务逻辑阻塞。
- 主从 Reactor 多线程:main Reactor(bossGroup)只负责 accept,多个 sub Reactor(workerGroup)各自分管一部分连接的读写,业务再交给线程池。Netty 默认就是这种模型,也是生产环境最常用的。
这种拆分的价值在于两层解耦:把「建连」和「读写」拆开,再把「读写」和「业务处理」拆开,单个报文解析慢不会拖住其他连接。
4. Netty 的核心组件有哪些(Channel、EventLoop、ChannelPipeline、ChannelHandler、ByteBuf、Bootstrap)★★★
六个核心组件:
- Channel:对 Socket 的抽象,代表一条连接,所有读写都通过它。
- EventLoop / EventLoopGroup:事件循环线程。一个 EventLoop 绑定一个线程,管理一组 Channel 的全部 IO 事件和任务;EventLoopGroup 是 EventLoop 的集合,也就是线程组。
- ChannelPipeline:责任链,链上挂着一串 ChannelHandler,数据按顺序流过。
- ChannelHandler:处理器,分**入站(Inbound)和出站(Outbound)**两类,负责编解码和业务处理。
- ByteBuf:Netty 的字节容器,比原生 ByteBuffer 好用——读写指针分离(不用 flip)、支持池化、支持引用计数。
- Bootstrap / ServerBootstrap:启动引导类,配置线程组、Channel 类型、Pipeline 等参数后,拉起客户端或服务端。
组件之间的关系是理解 Netty 的骨架:Bootstrap 创建 Channel → Channel 注册并绑定到一个 EventLoop → 数据在 Pipeline 的 Handler 链上流转 → Handler 内用 ByteBuf 读写数据。
5. ChannelPipeline 的作用、入站出站 handler 执行顺序 ★★★
ChannelPipeline 是一条双向链表结构的责任链。
- 入站事件(读数据):从链头往链尾依次触发 InboundHandler。
- 出站事件(写数据):从链尾往链头依次触发 OutboundHandler,方向与入站相反。
典型顺序:入站先粘包解码、再协议解码、最后业务处理;出站先协议编码、再帧编码。顺序一旦搞反,粘包就会处理错、报文解析就会乱。
写数据有两个容易混淆的入口:
- ChannelHandlerContext.write:从当前 handler 所在位置开始,往前(向链头方向)找 OutboundHandler,只有它之前的出站 handler 会执行。
- Channel.write:从链尾开始,走完整条 outbound 链。
两者起点不同,用错就会出现数据没走完整条编码链、报文格式不对的问题。
6. Netty 中如何解决 TCP 粘包/拆包 ★★★
TCP 是字节流,没有消息边界,所以会出现粘包(多个小包粘在一起)和拆包(一个大包被拆成多段)。
原因有三类:发送方的发送缓冲会合并小包、接收方读取不及时、MSS/MTU 限制导致大包被分片。
解决思路就三条,核心都是「在字节流里人为划出帧边界」:
- 定长消息:每条消息固定长度,不够补齐。
- 分隔符:用特殊字符标记消息结束,但消息内容不能包含该字符。
- 消息头带长度字段:先读长度,再按长度读满整个包,最通用。
Netty 内置了对应解码器,开箱即用:
- FixedLengthFrameDecoder:定长。
- DelimiterBasedFrameDecoder:分隔符。
- LengthFieldBasedFrameDecoder:长度字段,最常用。
注意:如果直接继承 ByteToMessageDecoder 而不做粘包处理,会出现一个 buffer 里塞了多个帧、或者半包解析错乱的问题。接自定义协议第一步就是把帧边界定清楚。
7. Netty 高性能表现在哪些方面 ★★★
高性能来自几个层面的叠加:
- Reactor 线程模型:少量线程撑起大量连接,减少线程创建与切换开销。worker 线程数远小于连接数,这就是多路复用的直接收益。
- 零拷贝:减少数据在内核态和用户态之间的搬运。
- 内存池:ByteBuf 池化复用,减少对象创建和 GC 压力;配合池化分配器还能控制直接内存占用,避免频繁创建导致 GC 抖动。
- 无锁化串行设计:同一 Channel 的 IO 事件和任务都在同一个 EventLoop 线程执行,天然不用加锁,避免锁竞争。
- 高效数据结构:FastThreadLocal、MPSC(多生产者单消费者)队列等。
这几项叠加起来就是高吞吐、低延迟。
8. Netty 的零拷贝技术如何实现 ★★★
Netty 的零拷贝不是字面上「完全不拷贝」,而是「减少不必要的拷贝」,可以从几个层面实现:
- 直接内存(堆外 ByteBuf):用堆外缓冲收发数据,省掉堆内与堆外之间的一次拷贝;默认用 PooledByteBufAllocator 池化分配。
- CompositeByteBuf:把多个 ByteBuf 组合成一个逻辑视图,不实际合并拷贝。
- FileRegion 的 transferTo:底层用操作系统 sendfile,文件数据从内核态直接发到 socket,不经过用户态。
- slice / wrap:对已有缓冲做切片和包装,都是视图操作,不复制数据。
核心是减少不必要的内存拷贝,而不是完全不拷贝。代价是堆外内存不在 GC 管辖范围,必须严格按引用计数 release 手动管理,否则会内存泄漏甚至直接内存 OOM。可用 io.netty.leakDetection.level 开启泄漏检测辅助排查。
9. Netty 的线程模型如何设计 ★★★
Netty 线程模型的核心是 EventLoop 体系:
- NioEventLoopGroup 是一组 EventLoop,每个 EventLoop 是一个单线程执行器,绑定一个线程。
- boss 线程组只负责 accept 连接,worker 线程组负责连接读写,这就是主从 Reactor 的落地形式。
- 一个 EventLoop 会被分配多个 Channel;同一个 Channel 的所有 IO 事件和任务都在同一个线程里串行执行,因此天然无锁,这就是无锁化设计的来源。
整个模型可以概括为:读写分离 + 事件循环 + 任务队列。
实践参数:bossGroup 一般设 1 个线程(只 accept 够用),workerGroup 按 CPU 核数 × 2 设置。连接建立后被固定分配到某个 worker EventLoop,之后该连接的所有报文读写、心跳超时检测都在同一线程执行,handler 里不用加锁。好处是线程少、无锁、上下文切换少。
10. Netty 的典型应用场景 ★★☆
Netty 适合高并发、长连接、自定义协议的通信场景:
- RPC 框架底层,如 Dubbo 默认用 Netty;
- 消息中间件,如 RocketMQ;
- 分布式协调组件,如 Zookeeper;
- API 网关、即时通讯、游戏服务器;
- 物联网设备接入:长连接 + 自定义协议 + 心跳保活。
不适合的场景是普通短连接 HTTP 接口——那是 Tomcat 这类 Servlet 容器的活,Netty 也能写 HTTP,但没必要。
判断标准:连接要长期保持、协议要自己定、单机连接数大,才轮到 Netty;一问一答的短连接请求交给 Servlet 容器更省事。
附:一句话速记(可以跳过 ★☆☆)
- BIO 一个连接一个线程,NIO 一个线程多个连接,Netty 是 NIO 的高层封装加优化。
- Reactor 三模式:单线程、多线程、主从多线程,Netty 默认主从多线程。
- 粘包拆包三个解法:定长、分隔符、长度字段,Netty 都有现成解码器。
- 零拷贝不是不拷贝,是少拷贝:直接内存、CompositeByteBuf、sendfile、slice。
- 一个 Channel 的所有事件都在同一个 EventLoop 线程执行,天然无锁。
Cao Note