Cluster 决定打谁、失败怎么办;真正把 Invocation 变成字节、在连接上多路复用并还原成对象的,是 Protocol + Serialization + Transport。这一层选错,表现是 CPU 打满、包体膨胀、跨语言互通失败,或「明明通了却对不上字段」。
本篇对比经典 Dubbo 协议与 Triple,讲清序列化选型,并落到广告热路径上的性能与兼容纪律。
本文是 Dubbo 深挖系列的第 4 篇。 ① 架构开篇 → ② 注册发现 → ③ Cluster → ④ 协议与 Triple(本篇) → ⑤ 流量治理。
一句话定位:协议决定帧与多路复用,序列化决定 CPU 与兼容;Triple 用 HTTP/2 把 Dubbo 推进云原生与 gRPC 互通,但不自动让你的接口变成跨语言友好。
TL;DR
- 经典 Dubbo 协议:自定义二进制头 + body,常跑在 Netty TCP 长连接上,Java 栈成熟、治理完整。
- Triple:基于 HTTP/2(及后续演进),对齐云原生与 gRPC 互操作,支持 Unary / Stream。
- 序列化:Hessian2(经典默认)、Protobuf(跨语言/强 schema)、Fastjson2/JSON(调试友好、热路径慎用)等可插拔。
- 性能三角:包体大小 × 序列化 CPU × 连接多路复用;竞价热路径优先紧凑二进制。
- 兼容纪律:字段只增不改语义、拒绝「同名不同类型」、跨版本双发布验证。
- AdTech:Java 内部可用 Dubbo+Hessian2;要对齐多语言特征服务/Mesh 时迁 Triple+Protobuf,别幻想「换协议名就完成现代化」。
Table of contents
Open Table of contents
1. 协议在栈里的位置
回顾开篇分层:Proxy / Cluster 之下是 Protocol。Protocol 的职责包括:
- export / refer:把服务暴露为可调用的 Invoker,或引用远端;
- 编解码:
Invocation↔ 字节帧; - 与 Transport 协作:连接管理、心跳、线程派发。
传输层细节与 Netty 编解码、连接与 I/O 模型 同族——粘包拆包、空闲踢除、背压,都会从这一层冒出来。
2. 经典 Dubbo 协议
要点:
- 请求关联:每个调用一个 requestId,异步回包能对上;
- 序列化类型进头:同集群混用多种序列化要能协商/一致;
- 长连接 + 多路:单连接上并发多个请求,省握手;
- 心跳:保活与摘除半死连接。
它为 Java 微服务优化得很深,但跨语言、被网关/Mesh 识别、与 gRPC 生态互通不是它的主场——这正是 Triple 的切入点。
3. Triple:为什么要 HTTP/2
3.1 Triple 带来什么
| 能力 | 意义 |
|---|---|
| HTTP/2 多路复用 / 流控 | 与 gRPC 同族传输语义 |
| Unary + Streaming | 服务端推、双向流等场景 |
| 更好的互通与可见性 | 易被网关、Mesh、观测识别 |
| IDL / Protobuf 友好 | 跨语言契约更清晰 |
3.2 Triple 不是「免费升级」
迁移成本真实存在:
- 接口是否适合 Protobuf/IDL 化;
- 老 Consumer 是否仍要双协议暴露;
- Filter/附件(attachment)语义是否等价;
- 超时、取消、流控与旧协议行为差异。
务实路径:新服务 Triple 优先;老服务双注册/双协议过渡;热路径单独压测序列化与 QPS。
4. 序列化选型
| 方案 | 特点 | 建议场景 |
|---|---|---|
| Hessian2 | Java 友好、历史默认 | 纯 Java 内部、存量 |
| Protobuf | 紧凑、强 schema、跨语言 | 竞价热路径、多语言 |
| Fastjson2 / JSON | 可读、好调试 | 边缘、低 QPS、排障旁路 |
| Kryo / FST 等 | 快但生态与安全边界要谨慎 | 仅内网可控环境评估 |
4.1 热路径怎么选
广告竞价 RPC 的症状很直接:
- CPU 高、GC 抖 → 序列化过重或对象图过深;
- 带宽打满 → 包体过大、重复传维表;
- 偶发字段错乱 → 兼容性破坏或两端序列化实现不一致。
原则:热路径二进制 schema;调试走旁路;禁止在热接口上「图省事用 JSON」。
4.2 兼容性门禁
- Protobuf:字段号稳定、只增不改语义、
reserved防复用; - Hessian:更灵活也更容易 silently 读错——跨团队接口要契约测试;
- 大促前禁止「顺手改公共 jar 字段类型」。
5. 附件、超时与上下文如何过协议
一次调用除了方法参数,还有:
- timeout / deadline;
- trace id / baggage;
- 路由标签、单元信息;
- 鉴权 token。
它们通常走 attachment / header,而不是业务参数。换协议时最容易丢的就是这类上下文——表现为「灰度失效」「链路断追踪」「服务端看不到超时剩余」。迁移 checklist 里应单列 上下文透传对照表。
6. 同步、异步与流式(协议视角)
- Unary:一请求一响应——扣预算、出价计算的主流;
- Server stream:服务端连续推——订阅类、大批量拉取;
- 双向流:长会话、交互式协议。
流式能减少「反复建调用」的开销,但会占用连接与服务端状态;广告计费写路径通常仍应 Unary + 明确超时,别为炫技上双向流。
7. 性能与排障清单
- 对比包体:同一业务对象在 Hessian2 vs Protobuf 下的大小;
- 火焰图:序列化是否占 CPU 大头;
- 连接数:实例数 × 连接是否爆炸;是否该连接复用/多路;
- 超时是否在服务端生效:还是只在客户端傻等;
- 大字段:把画像巨对象塞进同步 RPC,不如拆缓存或异步。
8. AdTech 演进样本
常见演进:
- 早期:Dubbo 协议 + Hessian2,纯 Java;
- 特征/实验平台多语言渗透 → Triple + Protobuf;
- 合规要求 mTLS / 统一治理 → Mesh 识别 HTTP/2;
- 入口与跨团队 API → 网关,内部仍直连 RPC。
每一阶段保留上一阶段仍高效的部分,比「全站某一天切协议」便宜一个数量级。
9. 生产反模式
- 热路径 JSON「缩进 pretty print」上生产。
- 两端序列化配置不一致,靠「碰巧能解」运行。
- 为上 Triple 强行把所有接口改 stream。
- 附件塞超大 payload,当「便宜的 side channel」。
- 忽略类加载与泛型擦除,线上
ClassNotFound阵发。
10. 常见误解 ↔ 正解
| 常见误解 | 正解 |
|---|---|
| Triple = 把 Dubbo 包进 HTTP | 传输、流、互通与生态位都变了 |
| Protobuf 一定更快 | 通常更紧凑;极简对象上差距可能不大,要压测 |
| 换序列化不用改 Consumer | 必须双端一致或可协商 |
| 协议层能解决幂等 | 幂等是业务 + Cluster 策略问题 |
| 长连接永不断 | NAT/空闲超时会断,要靠心跳与重连 |
11. 速查表
- Dubbo 协议:自定义帧 + TCP/Netty,Java 内部主力。
- Triple:HTTP/2,云原生与 gRPC 互通。
- 序列化:热路径紧凑二进制;JSON 旁路。
- 兼容:字段纪律 + 契约测试。
- 上下文:timeout/trace/tag 走附件,换协议要对表。
协议通了之后,最后一块是「流量怎么被规则塑造、发布如何不抖」——流量治理、Filter 链与优雅上下线。
延伸阅读
本系列内部串读:
相关:
一手资料:
- Apache Dubbo. Triple Protocol:Triple 官方说明。
- Apache Dubbo. Serialization:序列化扩展。
- gRPC. HTTP/2 Concepts:Unary/Stream 与 HTTP/2 心智对照。