Dubbo 支持的 RPC 通信协议
Dubbo 作为一款 RPC 框架内置了高效的 RPC 通信协议,帮助解决服务间的编码与通信问题,目前支持的协议包括:
- triple,基于 HTTP/1、HTTP/2 的高性能通信协议,100% 兼容 gRPC,支持 Unary、Streming 等通信模式;
- dubbo,基于 TCP 的高性能私有通信协议,缺点是通用性较差,更适合在 Dubbo SDK 间使用;
- rest,基于 HTTP+JSON 标准的通信协议,用于发布 REST 风格的 HTTP 服务,网关等路由组件兼容性高;
- 多种生态扩展协议;
协议概览
开发者该如何确定使用哪一种协议那? 以下是我们从使用场景、性能、编程易用性、多语言互通等方面对多个主流协议的对比分析:
协议 | 性能 | 网关友好 | Streaming通信模式 | 多语言互通 | 编程API | 说明 |
---|---|---|---|---|---|---|
triple | aaa | bbb | ccc | ddd | eee | fff |
dubbo | aaa | bbb | ccc | ddd | eee | fff |
rest | aaa | bbb | ccc | ddd | eee | fff |
注意
考虑到对过往版本的兼容性,当前 Dubbo 各个发行版本均默认使用dubbo
通信协议。但对于新用户而言,我们强烈建议在一开始就明确配置使用 triple
协议,老用户也尽快参考文档 实现协议的平滑迁移。以下是几个主要协议的具体开发、配置、运行态信息:
协议名称 | 配置值 | 服务定义方式 | 默认端口 | 传输层协议 | 序列化协议 | 是否默认 |
---|---|---|---|---|---|---|
triple | tri | - Java Interface - Protobuf(IDL) | 50051 | HTTP/1、HTTP/2 | Protobuf Binary、Protobuf JSON | 否 |
dubbo | dubbo | - Java Interface | 20880 | TCP | Hessian、Fastjson2、JSON、JDK、Avro、Kryo 等 | 是 |
rest | rest | - Java Interface + SpringMVC - Java Interface + JAX-RS | 8080 | HTTP/1、HTTP/2 | JSON | 否 |
接下来,我们一起看以下几个协议的基本使用方式。
Triple 协议
基本配置
通过以下配置启用 triple 协议,默认端口为 50051,如果设置 port: -1
则会随机选取端口(从 50051 自增,直到找到第一个可用端口)。
dubbo:
protocol:
name: tri
port: 50051
服务定义方式
使用 triple 协议时,开发者可以使用 Java Interface
、Protobuf(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 之外的其他语言,跨语言互通的场景是不是普遍? | Protobuf | Java 接口 |
公司里的开发人员是否熟悉 Protobuf,愿意接受 Protobuf 的额外成本吗? | Protobuf | Java 接口 |
是否有标准 gRPC 互通诉求? | Protobuf | Java 接口 |
是不是 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.
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.
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.
流的语义保证
- 提供消息边界,可以方便地对消息单独处理
- 严格有序,发送端的顺序和接收端顺序一致
- 全双工,发送不需要等待
- 支持取消和超时
关于 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);
}
注意
自 3.2.0 版本开始,Dubbo 增加了序列化协议的自动协商机制,如果满足条件 两端都为 Dubbo3 特定版本 + 存在 Fastjson2 相关依赖
,则会自动使用 fastjson2 序列化协议,否则使用 hessian2 协议,协商对用户透明无感。
由于 Dubbo2 默认序列化协议是 hessian2,对于部分有拦截rpc调用payload的场景,比如sidecar等对链路payload有拦截与解析,在升级过程中需留意兼容性问题。
- 关于 dubbo 协议的具体使用示例请参见【进阶学习 - 通信协议】中的 dubbo 协议示例。
HTTP 接入方式
由于 dubbo 协议无法支持 http 流量直接接入,因此需要有一层网关实现前端 http 协议到后端 dubbo 协议的转换过程(http -> dubbo
)。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
多协议发布
与 RPC 协议强相关的,Dubbo 支持从多个角度调整 RPC 调用时的行为,比如超时时间、线程池、连接数、负载大小、异步调用、隐式传参等,具体请查看 RPC 框架与 API 中说明。
除此之外,关于 RPC 协议还有以下相关链接可以参考:
- triple 协议规范
- dubbo 协议规范
- rest 协议规范