回顾

大家好,我是老马。

最近 dubbo3.x 在公司内部分享,于是想系统梳理一下。

总体思路是官方文档入门+一些场景的问题思考+源码解析学习。

协议概述

本文 triple 在 dubbo java 实现中的一些具体细节,配置方式、性能指标等

请参考文档其他部分了解 triple 协议规范规范基本使用方式

本文只展开 triple 协议 Java 实现中的一些具体细节内容。

编程模式

使用 triple 协议时,开发者可以使用 Java InterfaceProtobuf(IDL) 两种方式定义 RPC 服务,两种服务定义方式的协议能力是对等的,仅影响开发者的编程体验、序列化方式,具体选用那种开发模式,取决于使用者的业务背景。

Java接口

适合于 Dubbo 老用户、没有跨语言诉求的开发团队,具备学习成本低的优势,Dubbo2 老用户可以零成本切换到该协议

服务定义范例:

  [java]
1
2
3
public interface DemoService { String sayHello(String name); }

这种模式下,序列化方式可以选用 Hessian、JSON、Kryo、JDK、自定义扩展等任意编码协议。

在使用体验上,可以说与老版本 dubbo 协议没有任何区别,只需要改一个 protocol 配置项即可,因此对于 dubbo 协议迁移到 triple 也会更平滑。

请通过【进阶学习 - 通信协议】查看 java Interface + Triple 协议的具体使用示例

Protobuf

使用 Protobuf(IDL) 的方式定义服务,适合于当前或未来有跨语言诉求的开发团队,同一份 IDL 服务可同时用于 Java/Go/Node.js 等多语言微服务开发,劣势是学习成本较高

  [protobuf]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
syntax = "proto3"; option java_multiple_files = true; package org.apache.dubbo.springboot.demo.idl; message GreeterRequest { string name = 1; } message GreeterReply { string message = 1; } service Greeter{ rpc greet(GreeterRequest) returns (GreeterReply); }

通过 Dubbo 提供的 protoc 编译插件,将以上 IDL 服务定义预编译为相关 stub 代码,其中就包含 Dubbo 需要的 Interface 接口定义,因此在后续编码上区别并不大,只不过相比于前面的用户自定义 Java Interface 模式,这里由插件自动帮我们生成 Interface 定义。

  [java]
1
2
3
4
5
6
7
8
// Generated by dubbo protoc plugin public interface Greeter extends org.apache.dubbo.rpc.model.DubboStub { String JAVA_SERVICE_NAME = "org.apache.dubbo.springboot.demo.idl.Greeter"; String SERVICE_NAME = "org.apache.dubbo.springboot.demo.idl.Greeter"; org.apache.dubbo.springboot.demo.idl.GreeterReply greet(org.apache.dubbo.springboot.demo.idl.GreeterRequest request); // more generated codes here... }

Protobuf 模式支持序列化方式有 Protobuf Binary、Protobuf JSON 两种模式。最后,请通过【进阶学习 - 通信协议】查看 Protobuf (IDL) + Triple 协议的具体使用示例

3. 我该使用哪种编程模式,如何选择?

 
公司的业务是否有用 Java 之外的其他语言,跨语言互通的场景是不是普遍? Protobuf Java 接口
公司里的开发人员是否熟悉 Protobuf,愿意接受 Protobuf 的额外成本吗? Protobuf Java 接口
是否有标准 gRPC 互通诉求? Protobuf Java 接口
是不是 Dubbo2 老用户,想平滑迁移到 triple 协议? Java 接口 Protobuf

Streaming流式通信

流实现原理

Triple协议的流模式

  • 从协议层来说,Triple 是建立在 HTTP2 基础上的,所以直接拥有所有 HTTP2 的能力,故拥有了分 streaming 和全双工的能力。
  • 框架层来说,org.apache.dubbo.common.stream.StreamObserver 作为流的接口提供给用户,用于入参和出参提供流式处理。框架在收发 stream data 时进行相应的接口调用, 从而保证流的生命周期完整。

适用场景

Streaming 是 Dubbo3 新提供的一种调用类型,在以下场景时建议使用流的方式:

  • 接口需要发送大量数据,这些数据无法被放在一个 RPC 的请求或响应中,需要分批发送,但应用层如果按照传统的多次 RPC 方式无法解决顺序和性能的问题,如果需要保证有序,则只能串行发送
  • 流式场景,数据需要按照发送顺序处理, 数据本身是没有确定边界的
  • 推送类场景,多个消息在同一个调用的上下文中被发送和处理

Stream 分为以下三种。

SERVER_STREAM(服务端流)

服务端流式 RPC 类似于 Unary RPC,不同之处在于服务端会响应客户端的请求并返回消息流。在发送完所有消息后(通常是多条消息),服务端会发送状态信息(状态代码和可选状态消息)和可选的尾部元数据给客户端,这写状态信息发送完后服务器端流就结束了。一旦客户端通过 StreamObserver 接收到了以上所有了服务器消息,流就完成了。

服务端流

CLIENT_STREAM(客户端流)

客户端流式 RPC 类似于 Unary RPC,不同之处在于客户端向服务器发送消息流(通常包含多条消息)而不是单个消息。服务器以单个消息(以及其状态详细信息和可选的尾部元数据)进行响应 - 通常但不一定是在接收到所有客户端消息之后。

客户端流

BIDIRECTIONAL_STREAM(双向流)

在双向流 RPC 中,客户端发起方法调用,服务端则接收客户端调用中的元数据、方法名称和截止日期,这样就启动了一次完整的双向流通道。服务器可以选择返回其初始元数据,或者等待客户端开始流式传输消息。

客户端和服务器端的流处理是特定于应用程序的。由于这两个流是独立的,客户端和服务器可以按任何顺序读取和写入消息。例如,服务器可以等到收到客户端的所有消息后再写消息,或者服务器和客户端可以玩”乒乓球”——服务器收到一个请求,然后发回一个响应,然后客户端根据响应发送另一个请求等等。

双向流

流的语义保证

  • 提供消息边界,可以方便地对消息单独处理
  • 严格有序,发送端的顺序和接收端顺序一致
  • 全双工,发送不需要等待
  • 支持取消和超时

关于 Streaming 的具体使用示例,请参见 Streaming 流式通信

REST 支持

通过为 Java 接口增加注解,可以发布 rest 风格的 triple 服务,可在这里查看 具体代码示例

流的语义保证

目前 rest 协议仅支持 Java 接口 服务定义模式,相比于 dubbo 和 triple 协议,rest 场景下我们需要为 Interface 增加注解,支持 Spring MVC、JAX_RS 两种注解。

如果你记得 triple 协议原生支持 cURL 访问,即类似 org.apache.dubbo.springboot.demo.idl.Greeter/greet 的访问模式。通过增加以上注解后,即可为 triple 服务额外增加 REST 风格访问支持,如 demo/greet 的 GET 请求。

Spring Web注解

Spring MVC 服务定义范例:

  [java]
1
2
3
4
5
6
@RestController @RequestMapping("/demo") public interface DemoService { @GetMapping(value = "/hello") String sayHello(); }

JAX-RS注解

JAX-RS 服务定义范例:

  [java]
1
2
3
4
5
6
@Path("/demo") public interface DemoService { @GET @Path("/hello") String sayHello(); }

异常类型传递

Provider 端产生的业务异常需要作为响应值返回给 Consumer 客户端,消费端可以使用 try catch 捕获可能抛出的异常:

  [java]
1
2
3
4
5
6
7
try { greeterProxy.echo(REQUEST_MSG); } catch (YourCustomizedException e) { e.printStackTrace(); } catch (RpcException e) { e.printStackTrace(); }

Dubbo 框架会在 provider 侧根据如下流程发送异常类型响应,不是所有业务异常都能原样返回,对于无法处理的异常类型,都会被框架封装成 RpcException 类型返回:

triple-exception

附录

Protobuf与Java原生数据类型对比

对于计划从 Java 接口完全迁移到 Protobuf 的用户而言,这里的信息可供参考,用以了解类型迁移可能面临的限制,Protobuf 描述语言是否能完全描述 Java 数据类型。

本文对比了Protobuf和Java Interface这2种IDL的差异,帮助Dubbo协议开发者了解Protobuf,为后续转到Triple协议和Grpc协议做铺垫。

1. 数据类型

1.1. 基本类型
ptoto类型 java类型
double double
float float
int32 int
int64 long
uint32 int[注]
uint64 long[注]
sint32 int
sint64 long
fixed32 int[注]
fixed64 long[注]
sfixed32 int
sfixed64 long
bool boolean
string String
bytes ByteString

注意

在Java中,无符号的32位和64位整数使用它们的有符号对数来表示,顶部位只存储在符号位中。

2. 复合类型

2.1. 枚举
  [protobuf]
1
2
3
4
5
6
7
enum TrafficLightColor { TRAFFIC_LIGHT_COLOR_INVALID = 0; TRAFFIC_LIGHT_COLOR_UNSET = 1; TRAFFIC_LIGHT_COLOR_GREEN = 2; TRAFFIC_LIGHT_COLOR_YELLOW = 3; TRAFFIC_LIGHT_COLOR_RED = 4; }

image

枚举是常量,因此采用大写

2.2. 数组
  [protobuf]
1
2
3
message VipIDToRidReq { repeated uint32 vipID = 1; }

image

底层实际上是1个ArrayList

2.3. 集合

PB不支持无序、不重复的集合,只能 借用数组实现,需要 自行去重

2.4. 字典
  [protobuf]
1
2
3
message BatchOnlineRes { map<uint32, uint32> onlineMap = 1;//在线状态 }

image

2.5. 嵌套
  [protobuf]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
message BatchAnchorInfoRes { map<uint32, AnchorInfo> list = 1; //用户信息map列表 } /* * 对应接口的功能: 批量或单个获取用户信息 */ message AnchorInfo { uint32 ownerUid = 1 [json_name="uid"]; //用户id string nickName = 2 [json_name="nn"]; //用户昵称 string smallAvatar = 3 [json_name="savt"]; //用户头像全路径-小 string middleAvatar = 4 [json_name="mavt"]; //用户头像全路径-中 string bigAvatar = 5 [json_name="bavt"]; //用户头像全路径-大 string avatar = 6 [json_name="avt"]; //用户头像 }

image

3. 字段默认值

  • 对于字符串,默认值为空字符串。
  • 对于字节,默认值为空字节。
  • 对于bools,默认值为false。
  • 对于数字类型,默认值为零。
  • 对于枚举,默认值为第一个定义的枚举值,它必须为0。
  • 对于消息字段,未设置字段。 它的确切值是语言相关的。 有关详细信息,请参阅生成的代码指南。

4. 整体结构

Feature Java Interface Protobuf 备注
方法重载 ×  
泛型/模板化 ×  
方法继承 ×  
嵌套定义 部分支持 PB仅支持message和enum嵌套
import文件  
字段为null ×  
多个入参 × PB仅支持单入参
0个入参 × PB必须有入参
0个出参 × PB必须有出参
入参/出参为抽象类 × PB的入参/出参必须为具象类
入参/出参为接口 × PB的入参/出参必须为具象类
入参/出参为基础类型 × PB的入参/出参必须为结构体

5. 社区资料

  • 社区主页地址:https://developers.google.cn/protocol-buffers/
  • 社区开源地址:https://github.com/google/protobuf
  • 相关jar的maven:https://search.maven.org/search?q=com.google.protobuf

chat

triple 如何保证高性能

Dubbo 3.0 的 Triple 协议(基于 HTTP/2 + gRPC 标准扩展)的高性能主要源于其协议设计、传输层优化以及对现代 RPC 需求的深度适配。

以下是其高性能实现的关键技术点,以及它如何保证与传统 RPC 协议(如 Dubbo 2.x 的私有协议)相当甚至更高的效率:


1. 基于 HTTP/2 的传输层优化

  • 多路复用(Multiplexing)
    HTTP/2 支持在单个 TCP 连接上并发处理多个请求/响应(通过 Stream ID 区分),避免了传统 HTTP/1.1 的队头阻塞问题。这使得 Triple 协议在高并发场景下能显著减少连接数,降低资源消耗,提升吞吐量。
  • 头部压缩(HPACK)
    使用 HPACK 算法压缩 HTTP 头部,减少冗余数据传输,降低网络开销。
  • 二进制帧传输
    HTTP/2 的二进制帧格式比文本协议(如 HTTP/1.1)更高效,解析速度更快,适合高吞吐场景。

2. 高效的序列化机制

  • 默认支持 Protobuf(Protocol Buffers)
    Triple 协议默认使用 Protobuf 作为序列化方式,其二进制编码紧凑、解析速度快,显著减少数据体积和序列化/反序列化时间。
  • 兼容多种序列化扩展
    支持 JSON、Hessian2、Kryo 等序列化方式,用户可根据业务场景选择最优方案。

3. 协议层的精简设计

  • 精简协议头
    相比 Dubbo 2.x 的私有协议,Triple 协议通过复用 HTTP/2 的头部和标准 gRPC 协议定义,减少了自定义协议头的冗余字段,降低传输开销。
  • 统一元数据管理
    使用 HTTP/2 的 Header 传递元数据(如路由信息、超时配置),避免私有协议中复杂的二进制封装逻辑。

4. 全双工通信与流式支持

  • 支持 Reactive Stream 模型
    Triple 协议基于 HTTP/2 的流式特性,实现了请求/响应流、客户端流、服务端流、双向流四种通信模式,适用于大数据量分片传输或实时交互场景(如物联网、实时日志流)。
  • 异步非阻塞 IO
    结合 Netty 等异步框架,Triple 协议在传输层实现了非阻塞 IO,充分利用多核 CPU,避免线程阻塞。

5. 服务治理与性能调优

  • 无缝集成 Dubbo 服务治理
    Triple 协议兼容 Dubbo 的负载均衡、熔断、限流等机制,确保高性能的同时保障服务稳定性。
  • 连接池优化
    通过复用 HTTP/2 的长连接,减少 TCP 握手和 TLS 协商的开销,提升连接利用率。
  • 线程模型优化
    Dubbo 3.0 的 I/O 线程与业务线程分离,避免业务逻辑阻塞网络通信。

6. 对云原生的深度适配

  • 兼容 gRPC 生态
    Triple 协议完全兼容 gRPC 标准,可直接与 gRPC 服务互通,复用 gRPC 网关、监控等工具链。
  • 支持 TLS 加密与 ALPN 协商
    通过标准化的 TLS 加密和 ALPN(应用层协议协商),无缝适配 Service Mesh(如 Istio)等云原生基础设施。

7. 与传统 RPC 的性能对比

  • 优势场景
    • 高并发场景:HTTP/2 多路复用显著优于传统 RPC 的单一连接模型。
    • 流式处理:传统 RPC 通常仅支持简单请求-响应,而 Triple 的流式能力更适合复杂交互。
  • 性能持平
    在简单请求-响应场景下,Triple 协议通过优化序列化和协议头,性能与 Dubbo 2.x 私有协议基本持平。

总结

Triple 协议的高性能来源于对 HTTP/2 底层能力的充分利用、协议层的精简设计、高效的序列化机制,以及对云原生架构的深度适配。

它既保留了传统 RPC 的高效性,又通过标准化协议扩展了流式通信和跨语言互通能力,成为 Dubbo 3.0 面向未来的核心协议。

对于开发者而言,Triple 协议无需牺牲性能即可获得更强大的功能和更广泛的生态兼容性。

参考资料

https://cn.dubbo.apache.orghttps://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/protocol/overview/