Netty 有哪些序列化协议?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
扩展机制理解:面试官真正想问的是 —— Netty 本身没有绑定任何序列化协议,它是通过 Codec(编解码器)框架把序列化能力抽象出来的。你是否理解
MessageToMessageCodec、ByteToMessageDecoder这些扩展点? -
生态熟悉度:Java 圈主流的序列化方案有哪些,各自的优缺点、适用场景能不能说清楚?
-
选型能力:不同业务场景下该怎么选 —— 内部 RPC 用 Protobuf、和前端联调用 JSON、跨语言用 MessagePack、临时调试用 Java 原生……这是工程能力。
核心答案
先一句话划重点:Netty 本身不绑定序列化协议,它只负责把字节流通过网络传输,至于字节流里装的是什么、怎么编出来的,完全交给 Codec 体系。
下面这张表把主流方案拉出来对比:
| 序列化协议 | Netty 内置支持 | 跨语言 | 性能 | 体积 | 典型场景 |
|---|---|---|---|---|---|
| Java 原生 | ✅ ObjectEncoder/ObjectDecoder |
❌ | 差 | 大 | 调试、内部系统 |
| JSON (Jackson/Gson/Fastjson) | ❌ 自己实现 Codec | ✅ | 中 | 偏大 | HTTP API、对前端 |
| Protobuf | ✅ 全套 Encoder/Decoder | ✅ | 优 | 极小 | RPC、IM、移动端 |
| MessagePack | ❌ 自实现或第三方 | ✅ | 优 | 小 | 替代 JSON 的二进制场景 |
| Kryo | ❌ 自己实现 Codec | ❌ | 优 | 小 | Java 内部高性能 RPC |
| Hessian | ❌ 自己实现 Codec | 部分 | 中 | 中 | Dubbo 早期默认(Hessian2) |
| JBoss Marshalling | ✅ MarshallingEncoder/MarshallingDecoder |
❌ | 中 | 中 | JBoss 内部 |
| Thrift / Avro | ❌ 自己实现 Codec | ✅ | 优 | 小 | 跨语言 RPC |
Netty 开箱即用的只有三种:Java 原生、Protobuf、JBoss Marshalling。其他的都得自己写 Codec,但写起来不难——继承 MessageToMessageCodec 重写两个方法就行。
深度解析
一、Netty 的编解码架构
先理解 Netty 处理序列化的位置 —— 它是在 ChannelPipeline 里挂编解码 Handler:
Netty 处理序列化的关键链路拆开看:
- 编码方向(出站):业务对象 → Encoder → ByteBuf → Socket
- 解码方向(入站):Socket → ByteBuf → 拆包/粘包处理 → Decoder → 业务对象
- 粘包处理是必须的:TCP 是流式协议,序列化出来的字节流必须配合
LengthFieldBasedFrameDecoder之类的拆包器,才能正确还原一个个完整消息
所以 Netty 内置 Protobuf 支持的时候,配套了两个看起来 "重复" 的 Handler —— 一个负责按 Varint32 拆包,一个负责把拆出来的字节反序列化成 Protobuf 对象。
二、Java 原生序列化
最简单也最不推荐:
// Netty Pipeline 里直接挂上即可
pipeline.addLast(new ObjectEncoder());
pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
优点是零依赖、写起来快,缺点一抓一大把:
- 不支持跨语言:序列化后只有 JVM 能读,跟 Go、Node 服务没法对接
- 体积大:每个对象都要带类信息,光一个 String "hello" 都能序列化出几十字节
- 性能差:反射 + 大量临时对象,GC 压力大
- 安全漏洞:反序列化漏洞历史上一堆 RCE(比如著名的 Apache Commons Collections 链)
所以阿里手册里明确写了:禁止使用 Java 原生序列化进行存储与网络传输。但调试本地小工具还是好用的。
三、Protobuf(Netty 内置,面试重点)
这是 Google 开源的二进制序列化协议,也是 Netty 唯一内置了完整工具链的非 Java 方案。
典型 Pipeline 配置:
// 粘包处理:在消息头加一个 Varint32 表示长度
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
// Protobuf 编码:Protobuf 对象 → 字节
pipeline.addLast(new ProtobufEncoder());
// 粘包处理:按 Varint32 拆出完整帧
pipeline.addLast(new ProtobufVarint32FrameDecoder());
// Protobuf 解码:字节 → Protobuf 对象
pipeline.addLast(new ProtobufDecoder(UserInfo.User.getDefaultInstance()));
注意最后一行——解码器必须传一个默认实例(getDefaultInstance()),告诉它要反序列化成哪种类型。Protobuf 不像 JSON 那样能从内容推断类型,它依赖 .proto 文件预生成的 Java 类。
Protobuf 为啥这么能打:
- 强类型 + IDL:
.proto文件就是契约,跨语言、跨团队对接不易出错 - 体积小:用 Varint、Tag 等技巧压缩数字和字段名
- 快:编译期生成硬编码的序列化代码,比反射快一个数量级
- 向前向后兼容:字段编号机制让增删字段不破坏老代码
四、JSON(最常用但 Netty 不内置)
Web 场景下 JSON 是绕不开的。Netty 没有内置 JSON 的 Codec,得自己包一层:
public class JsonDecoder<T> extends MessageToMessageDecoder<String> {
private final Class<T> targetType;
private final ObjectMapper mapper = new ObjectMapper();
public JsonDecoder(Class<T> targetType) {
this.targetType = targetType;
}
@Override
protected void decode(ChannelHandlerContext ctx, String msg, List<Object> out)
throws Exception {
// 先用 StringDecoder 把 ByteBuf 转成字符串,再反序列化
out.add(mapper.readValue(msg, targetType));
}
}
实际项目里更常见的是用 HttpServerCodec + 业务自定义的 JSON 处理。如果只追求可读性、调试方便、跟前端对接,JSON 完全够用。但对 RPC 场景来说 JSON 是反面教材——体积大、解析慢、字段名重复传输。
五、其他主流方案速览
- MessagePack:自称"二进制 JSON",比 JSON 紧凑很多,性能接近 Protobuf,但跨语言生态略弱。适合想用 JSON 风格又想要性能的场景。
- Kryo:Java 圈高性能序列化的标杆,速度奇快、体积小。缺点是不支持跨语言,注册机制用错容易出 bug。两个坑要记住:Kryo 对象非线程安全,要么用 ThreadLocal,要么用对象池;被序列化的类没无参构造函数,Kryo 会退化为 Java 序列化,性能直接崩。早期 Dubbo 把它列为高性能可选方案(默认是 Hessian2)。
- Hessian:Caucho 出品,Dubbo 早期的默认协议(Hessian2)。跨语言支持一般,性能中等,胜在协议老、稳定。
- JBoss Marshalling:JBoss 内部的 Java 对象序列化方案,比 Java 原生略好但仍是 Java only,Netty 内置了支持。实际项目里用得不多。
- Thrift / Avro:跟 Protobuf 一个赛道的,跨语言、IDL 驱动。Thrift 还自带完整的 RPC 框架。
六、选型决策建议
实操上的几条经验:
- 跨语言 RPC:闭眼选 Protobuf,生态最完整
- 跟前端 / 第三方对接:JSON 没得选,老老实实写 Codec
- 纯 Java 内部高性能:Kryo,但要管理好
Kryo实例池(非线程安全) - 临时调试 / Demo:Java 原生序列化,别在生产用
- 替代 JSON 的二进制场景:MessagePack
面试高频追问
- Netty 怎么解决 TCP 粘包/半包问题?
- 配合
LengthFieldBasedFrameDecoder、LineBasedFrameDecoder、DelimiterBasedFrameDecoder等拆包器。Protobuf 配套的是ProtobufVarint32FrameDecoder。
- 为什么 Netty 内置了 Protobuf 却不内置 JSON?
- Protobuf 是二进制格式,必须配套 IDL 和拆包规则,工具链不可拆分;JSON 本身是字符串,可以直接复用 Netty 的
StringDecoder+ 业务自定义 Codec,没必要在框架层内置。
- Protobuf 为什么序列化后体积小?
- 字段用 Tag 编号代替名字、数字用 Varint 压缩、字段省略(默认值不传输)。
- Kryo 和 Protobuf 怎么选?
- 要跨语言就 Protobuf;纯 Java、追求极致性能就 Kryo。但 Kryo 不支持向前向后兼容,改字段要格外小心。
常见面试变体
- "Netty 中如何自定义一个编解码器?"
- "Protobuf 和 JSON 在性能上差多少?"
- "为什么 RPC 框架基本都用 Protobuf 而不是 JSON?"
- "如何在 Netty 中实现多种序列化协议的动态切换?"
记忆口诀
Netty 不绑序列化,Codec 框架靠扩展;内置只有三件套 —— Java、Protobuf、Marshalling;选型先看跨不跨语言,再看性能与体积。
总结
一句话答完这道题:Netty 不绑定任何序列化协议,它通过 Codec 体系让用户自由接入。内置支持的有 Java 原生、Protobuf、JBoss Marshalling 三种,主流的 JSON、MessagePack、Kryo、Hessian 等都需要自己实现 Codec。生产 RPC 场景基本就是 Protobuf 的天下,其他场景就在跨语言、性能、可读性之间做权衡。把"Netty 是框架不是序列化方案"这个本质搞清楚,这道题就稳了。
