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+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 扩展机制理解:面试官真正想问的是 —— Netty 本身没有绑定任何序列化协议,它是通过 Codec(编解码器)框架把序列化能力抽象出来的。你是否理解 MessageToMessageCodecByteToMessageDecoder 这些扩展点?

  2. 生态熟悉度:Java 圈主流的序列化方案有哪些,各自的优缺点、适用场景能不能说清楚?

  3. 选型能力:不同业务场景下该怎么选 —— 内部 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 编解码流程
Netty 编解码流程

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 框架。

六、选型决策建议

Netty 序列化方案选型
Netty 序列化方案选型

实操上的几条经验:

  • 跨语言 RPC:闭眼选 Protobuf,生态最完整
  • 跟前端 / 第三方对接:JSON 没得选,老老实实写 Codec
  • 纯 Java 内部高性能:Kryo,但要管理好 Kryo 实例池(非线程安全)
  • 临时调试 / Demo:Java 原生序列化,别在生产用
  • 替代 JSON 的二进制场景:MessagePack

面试高频追问

  1. Netty 怎么解决 TCP 粘包/半包问题?
  • 配合 LengthFieldBasedFrameDecoderLineBasedFrameDecoderDelimiterBasedFrameDecoder 等拆包器。Protobuf 配套的是 ProtobufVarint32FrameDecoder
  1. 为什么 Netty 内置了 Protobuf 却不内置 JSON?
  • Protobuf 是二进制格式,必须配套 IDL 和拆包规则,工具链不可拆分;JSON 本身是字符串,可以直接复用 Netty 的 StringDecoder + 业务自定义 Codec,没必要在框架层内置。
  1. Protobuf 为什么序列化后体积小?
  • 字段用 Tag 编号代替名字、数字用 Varint 压缩、字段省略(默认值不传输)。
  1. 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 是框架不是序列化方案"这个本质搞清楚,这道题就稳了。