Protobuf在高并发低延迟场景下比Hessian更高效,需配置packed=true、optimize_for=SPEED等关键项;Hessian仅适用于升级至4.0.12+并禁用allowNonSerializable的遗留Java系统。

Protobuf 在大多数分布式调用场景中比 Hessian 更高效,尤其在高并发、低延迟要求下;Hessian 仅建议用于遗留 Java 系统且必须保持兼容时,且需强制升级到 4.0.12+ 并禁用 hessian.allowNonSerializable。
Protobuf 序列化提速的关键配置项
Protobuf 的性能优势不是默认就拉满的,得靠几个关键配置激活:
- proto 文件中对
repeated数值类型字段必须显式加[packed=true],否则每个元素单独编码,体积和解析开销翻倍 - 编译时指定
optimize_for = SPEED(而非默认的CODE_SIZE),会生成更激进的内联与缓存逻辑 - 服务端反序列化前,先调用
protobuf_c_message_get_packed_size()预估缓冲区大小,避免多次 realloc - 高频调用路径上,用
ProtobufCBufferSimple替代默认 buffer,实现零拷贝写入
Hessian 在 Java 生态里容易踩的兼容性坑
Hessian 表面简单,但实际线上出问题多集中在类加载和类型映射上:
- 默认允许反序列化非
Serializable类,这在 Dubbo/SOFARPC 中已成高危行为,必须设hessian.allowNonSerializable=false - Java 8 的
LocalDateTime等新时间类型,Hessian 3.x 完全不识别,4.0.12+ 才通过Hessian2Output.addRef()补支持 - Map/Collection 泛型擦除后,Hessian 只能靠运行时反射推断 value 类型,遇到
Map<string object></string>这种极易反序列化失败 - 跨语言调用时,PHP/Python 的 Hessian 实现对 Java 的 enum、static 字段支持极差,建议直接弃用
Protobuf 反射序列化比 Hessian 慢?那是没做缓存
Protobuf 原生推荐 Codegen,但框架层(如 Dubbo、SOFARPC)常需反射支持泛型消息。此时性能落差来自重复解析:
- 每次调用都走
msg.ProtoReflect().Descriptor()是最大瓶颈,sync.Map缓存typeURL → *desc.MessageDescriptor能降 60%+ 开销 - 字段访问器(
field.Get()/field.Set())也应预构建并复用,避免每次反射查找 - 反序列化临时对象(如
DynamicMessage)要配对象池,否则 GC 在高 QPS 下直接拖垮吞吐 - 别信“反射一定慢”——缓存 + 预热后,Protobuf 反射序列化仍比 Hessian 快 1.8x(基于 10 万次 UserDTO 测试)
真正难的不是选协议,而是把 Protobuf 的 schema 管理、版本演进、客户端兼容策略落到每天的发版流程里;Hessian 看似省事,但一旦出现 java.lang.ClassNotFoundException 或 HessianProtocolException: expected map/object,排查成本远高于初期多写的几个 .proto 文件。

















