Dubbo 支持的 RPC 通信协议

Dubbo 是一款轻量的 RPC 框架,提供 Java、Go、Node.js、Javascript 等语言支持,帮助开发者构建浏览器、gRPC 兼容的 HTTP API。

Dubbo 作为一款 RPC 框架内置了高效的 RPC 通信协议,帮助解决服务间的编码与通信问题,目前支持的协议包括:

  • triple,基于 HTTP/1、HTTP/2 的高性能通信协议,100% 兼容 gRPC,支持 Unary、Streming 等通信模式;
  • dubbo,基于 TCP 的高性能私有通信协议,缺点是通用性较差,更适合在 Dubbo SDK 间使用;
  • rest,基于 HTTP+JSON 标准的通信协议,用于发布 REST 风格的 HTTP 服务,网关等路由组件兼容性高;
  • 多种生态扩展协议

协议概览

开发者该如何确定使用哪一种协议那? 以下是我们从使用场景、性能、编程易用性、多语言互通等方面对多个主流协议的对比分析:

协议性能网关友好Streaming通信模式多语言互通编程API说明
tripleaaabbbcccdddeeefff
dubboaaabbbcccdddeeefff
restaaabbbcccdddeeefff

以下是几个主要协议的具体开发、配置、运行态信息:

协议名称配置值服务定义方式默认端口传输层协议序列化协议是否默认
tripletri- Java Interface
- Protobuf(IDL)
50051HTTP/1、HTTP/2Protobuf Binary、Protobuf JSON
dubbodubbo- Java Interface20880TCPHessian、Fastjson2、JSON、JDK、Avro、Kryo 等
restrest- Java Interface + SpringMVC
- Java Interface + JAX-RS
8080HTTP/1、HTTP/2JSON

接下来,我们一起看以下几个协议的基本使用方式。

Triple 协议

基本配置

通过以下配置启用 triple 协议,默认端口为 50051,如果设置 port: -1 则会随机选取端口(从 50051 自增,直到找到第一个可用端口)。

dubbo:
 protocol:
   name: tri
   port: 50051

服务定义方式

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

1. Java Interface

即通过声明一个 Java 接口的方式定义服务,我们在快速开始一节中看到的示例即是这种模式,适合于没有跨语言诉求的开发团队,具备学习成本低的优势,Dubbo2 老用户可以零成本切换协议

服务定义范例:

public interface DemoService {
    String sayHello(String name);
}

这种模式下,序列化方式可以选用 Hessian、JSON、Kryo、JDK、自定义扩展等任意编码协议。在使用体验上,可以说与老版本 dubbo 协议没有任何区别,只需要改一个 protocol 配置项即可,因此对于 dubbo 协议迁移到 triple 也会更平滑。

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

2. Protobuf(IDL)

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

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 定义。

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

HTTP 接入方式

triple 协议支持标准 HTTP 工具的直接访问,因此前端组件如浏览器、网关等接入非常边便捷,同时服务测试也变得更简单。

当服务启动后,可以使用 cURL 命令直接访问:

curl \
    --header "Content-Type: application/json" \
    --data '["Dubbo"]' \
    http://localhost:50052/org.apache.dubbo.springboot.demo.idl.Greeter/greet/

以上默认使用 org.apache.dubbo.springboot.demo.idl.Greeter/greet 这种 HTTP 访问路径,且仅支持 post 方法,如果你想对外发布 REST 风格服务,请参考下文 REST 协议小节。

Streaming 流式通信模式

流实现原理

Triple协议的流模式

  • 从协议层来说,Triple 是建立在 HTTP2 基础上的,所以直接拥有所有 HTTP2 的能力,故拥有了分 streaming 和全双工的能力。

  • 框架层来说,StreamObserver 作为流的接口提供给用户,用于入参和出参提供流式处理。框架在收发 stream data 时进行相应的接口调用, 从而保证流的生命周期完整。

适用场景

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

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

Stream 分为以下三种。

SERVER_STREAM(服务端流)

A server-streaming RPC is similar to a unary RPC, except that the server returns a stream of messages in response to a client’s request. After sending all its messages, the server’s status details (status code and optional status message) and optional trailing metadata are sent to the client. This completes processing on the server side. The client completes once it has all the server’s messages. SERVER_STREAM

CLIENT_STREAM(客户端流)

A client-streaming RPC is similar to a unary RPC, except that the client sends a stream of messages to the server instead of a single message. The server responds with a single message (along with its status details and optional trailing metadata), typically but not necessarily after it has received all the client’s messages. CLIENT_STREAM

BIDIRECTIONAL_STREAM(双向流)

In a bidirectional streaming RPC, the call is initiated by the client invoking the method and the server receiving the client metadata, method name, and deadline. The server can choose to send back its initial metadata or wait for the client to start streaming messages.

Client- and server-side stream processing is application specific. Since the two streams are independent, the client and server can read and write messages in any order. For example, a server can wait until it has received all of a client’s messages before writing its messages, or the server and client can play “ping-pong” – the server gets a request, then sends back a response, then the client sends another request based on the response, and so on. BIDIRECTIONAL_STREAM

关于 Streaming 的具体使用示例,请参见 【进阶学习 - 通信协议 - Streaming 流式通信】。

Dubbo 协议

基本配置

通过以下配置启用 dubbo 协议,默认端口为 20880,如果设置 port: -1 则会随机选取端口(从 20880 自增,直到找到第一个可用端口)。

dubbo:
 protocol:
   name: dubbo
   port: 20880

服务定义方式

dubbo 协议支持使用 Java Interface 方式定义 Dubbo RPC 服务,即通过声明一个 Java 接口的方式定义服务。序列化方式可以选用 Hessian、Fastjson2、JSON、Kryo、JDK、自定义扩展等任意编码协议,默认序列化协议为 Fastjson2。

public interface DemoService {
    String sayHello(String name);
}
  • 关于 dubbo 协议的具体使用示例请参见【进阶学习 - 通信协议】中的 dubbo 协议示例

HTTP 接入方式

由于 dubbo 协议无法支持 http 流量直接接入,因此需要有一层网关实现前端 http 协议到后端 dubbo 协议的转换过程(http -> dubbo)。Dubbo 框架提供了 泛化调用 能力,可以让网关在无服务接口定义的情况下对后端服务发起调用。

http -> dubbo 架构图

目前社区有很多开源网关产品支持 http -> dubbo 的,它们大部分都提供了可视化界面配置参数映射(泛化调用),同时还支持基于 Nacos、Zookeeper 等主流注册中心的自动地址发现,具体请查看 【进阶学习 - HTTP网关接入】

  • Higress
  • Pixiu
  • APISIX
  • Shenyu
  • Tengine

REST 协议

通过以下配置启用 rest 协议,默认端口为 8080,如果设置 port: -1 则会随机选取端口(从 8080 自增,直到找到第一个可用端口)。

dubbo:
 protocol:
   name: rest
   port: 8080

服务定义方式

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

Spring MVC 服务定义范例:

@RestController
@RequestMapping("/demo")
public interface DemoService {
    @GetMapping(value = "/hello")
    String sayHello();
}

JAX-RS 服务定义范例:

@RestController
@RequestMapping("/demo")
public interface DemoService {
    @GetMapping(value = "/hello")
    String sayHello();
}

如果你记得 triple 协议原生支持 cURL 访问,即类似 org.apache.dubbo.springboot.demo.idl.Greeter/greet 的访问模式。通过增加以上注解并将服务同时发布到 triple 协议、rest 协议,可以为 triple 服务额外增加 REST 风格访问支持。

dubbo:
 protocols:
   - name: rest
     port: 8080
   - name: triple
     port: 50051
  • 关于 rest 协议的具体使用示例请参见【进阶学习 - 通信协议】中的 rest 协议示例
  • 关于 Dubbo 多协议支持的更多细节请参见【进阶学习 - 通信协议】中的 多协议发布

多协议发布

与 RPC 协议强相关的,Dubbo 支持从多个角度调整 RPC 调用时的行为,比如超时时间、线程池、连接数、负载大小、异步调用、隐式传参等,具体请查看 RPC 框架与 API 中说明。

除此之外,关于 RPC 协议还有以下相关链接可以参考:

  • triple 协议规范
  • dubbo 协议规范
  • rest 协议规范

最后修改 January 11, 2024: update (#2902) (705faa9cd7)