Dubbo 在跨语言和协议穿透性方向上的探索:支持 HTTP/2 gRPC 和 Protobuf
本文整理自刘军在 Dubbo 成都 meetup 上分享的《Dubbo 在多语言和协议穿透性方向上的探索》。
本文总体上可分为基础产品简介、Dubbo 对 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分,在简介部分介绍了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特点;第二部分介绍了 Dubbo 为何要支持 gRPC (HTTP/2) 和 Protobuf,以及这种支持为 gRPC 和 Dubbo 开发带来的好处与不同;第三部分通过两个实例分别演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。
基本介绍
Dubbo 协议
从协议层面展开,以下是当前 2.7 版本支持的 Dubbo 协议
众所周知,Dubbo 协议是直接定义在 TCP 传输层协议之上,由于 TCP 高可靠全双工的特点,为 Dubbo 协议的定义提供了最大的灵活性,但同时也正是因为这样的灵活性,RPC 协议普遍都是定制化的私有协议,Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方,关于协议详细解析请参见官网博客
- Dubbo 协议体 Body 中有一个可扩展的 attachments 部分,这给 RPC 方法之外额外传递附加属性提供了可能,是一个很好的设计。但是类似的 Header 部分,却缺少类似的可扩展 attachments,这点可参考 HTTP 定义的 Ascii Header 设计,将 Body Attachments 和 Header Attachments 做职责划分。
- Body 协议体中的一些 RPC 请求定位符如 Service Name、Method Name、Version 等,可以提到 Header 中,和具体的序列化协议解耦,以更好的被网络基础设施识别或用于流量管控。
- 扩展性不够好,欠缺协议升级方面的设计,如 Header 头中没有预留的状态标识位,或者像 HTTP 有专为协议升级或协商设计的特殊 packet。
- 在 Java 版本的代码实现上,不够精简和通用。如在链路传输中,存在一些语言绑定的内容;消息体中存在冗余内容,如 Service Name 在 Body 和 Attachments 中都存在。
HTTP/1
相比于直接构建与 TPC 传输层的私有 RPC 协议,构建于 HTTP 之上的远程调用解决方案会有更好的通用性,如WebServices 或 REST 架构,使用 HTTP + JSON 可以说是一个事实标准的解决方案。
之所有选择构建在 HTTP 之上,我认为有两个最大的优势:
- HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。
- 通用性,HTTP 协议几乎被网络上的所有设备所支持,具有很好的协议穿透性。
具体来说,HTTP/1 的优势和限制是:
- 典型的 Request – Response 模型,一个链路上一次只能有一个等待的 Request 请求
- HTTP/1 支持 Keep-Alive 链接,避免了链接重复创建开销
- Human Readable Headers,使用更通用、更易于人类阅读的头部传输格式
- 无直接 Server Push 支持,需要使用 Polling Long-Polling 等变通模式
HTTP/2
HTTP/2 保留了 HTTP/1 的所有语义,在保持兼容的同时,在通信模型和传输效率上做了很大的改进。
- 支持单条链路上的 Multiplexing,相比于 Request - Response 独占链路,基于 Frame 实现更高效利用链路
- Request - Stream 语义,原生支持 Server Push 和 Stream 数据传输
- Flow Control,单条 Stream 粒度的和整个链路粒度的流量控制
- 头部压缩 HPACK
- Binary Frame
- 原生 TLS 支持
gRPC
上面提到了在 HTTP 及 TCP 协议之上构建 RPC 协议各自的优缺点,相比于 Dubbo 构建于 TPC 传输层之上,Google 选择将 gRPC 直接定义在 HTTP/2 协议之上,关于 gRPC 的 基本介绍和 设计愿景请参考以上两篇文章,我这里仅摘取 设计愿景 中几个能反映 gRPC 设计目的特性来做简单说明。
- Coverage & Simplicity,协议设计和框架实现要足够通用和简单,能运行在任何设备之上,甚至一些资源首先的如 IoT、Mobile 等设备。
- Interoperability & Reach,要构建在更通用的协议之上,协议本身要能被网络上几乎所有的基础设施所支持。
- General Purpose & Performant,要在场景和性能间做好平衡,首先协议本身要是适用于各种场景的,同时也要尽量有高的性能。
- Payload Agnostic,协议上传输的负载要保持语言和平台中立。
- Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。
- Flow Control,协议自身具备流量感知和限制的能力。
- Metadata Exchange,在 RPC 服务定义之外,提供额外附加数据传输的能力。
总的来说,在这样的设计理念指导下,gRPC 最终被设计为一个跨语言、跨平台的、通用的、高性能的、基于 HTTP/2 的 RPC 协议和框架。
Protobuf
Protocol buffers (Protobuf) 是 Google 推出的一个跨平台、语言中立的结构化数据描述和序列化的产品,它定义了一套结构化数据定义的协议,同时也提供了相应的 Compiler 工具,用来将语言中立的描述转化为相应语言的具体描述。
它的一些特性包括:
- 跨语言 跨平台,语言中立的数据描述格式,默认提供了生成多种语言的 Compiler 工具。
- 安全性,由于反序列化的范围和输出内容格式都是 Compiler 在编译时预生成的,因此绕过了类似 Java Deserialization Vulnarability 的问题。
- 二进制 高性能
- 强类型
- 字段变更向后兼容
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone =