摘要
Dubbo3 Triple 协议是参考 gRPC、gRPC-Web、Dubbo2 等协议特点设计而来,它吸取各自协议特点,完全兼容 gRPC、Streaming 通信、且无缝支持 HTTP/1 和浏览器。
当你在 Dubbo 框架中使用 Triple 协议,然后你就可以直接使用 Dubbo 客户端、gRPC 客户端、curl、浏览器等访问你发布的服务,不需要任何额外组件与配置。
除易用性以外,Dubbo3 Triple 在性能调优方面做了大量工作,本文将侧重对 Triple 协议背后的高性能秘密进行深入讲解,涉及一些有价值的性能调优工具、技巧及代码实现;在下一篇文章中,我们将具体展开 Triple 协议在易用性方面的一些具体使用场景。
为什么要优化 Triple 协议的性能?
自 2021 年开始 Dubbo3 就已经作为下一代服务框架逐步开始取代阿里内部广泛使用的 HSF 框架,截止目前,阿里以淘宝、天猫等电商为代表的绝大多数核心应用已经成功升级到 Dubbo3。作为过去两年支撑阿里双十一万亿级服务调用的关键框架,Triple 通信协议的性能直接影响整个系统的运行效率。
前置知识
1. Triple 协议简介
Triple 协议是参考 gRPC 与 gRPC-Web 两个协议设计而来,它吸取了两个协议各自的特性和优点,将它们整合在一起,成为一个完全兼容 gRPC 且支持 Streaming 通信的协议,同时 Triple 还支持 HTTP/1、HTTP/2。
Triple 协议的设计目标如下:
- Triple 设计为对人类友好、开发调试友好的一款基于 HTTP 的协议,尤其是对 unary 类型的 RPC 请求。
- 完全兼容基于 HTTP/2 的 gRPC 协议,因此 Dubbo Triple 协议实现可以 100% 与 gRPC 体系互调互通。
当你在 Dubbo 框架中使用 Triple 协议,然后你就可以直接使用 Dubbo 客户端、gRPC 客户端、curl、浏览器等访问你发布的服务。
以下是使用 curl 客户端访问 Dubbo 服务端一个 Triple 协议服务的示例:
curl \
--header "Content-Type: application/json"\
--data '{"sentence": "Hello Dubbo."}'\
https://host:port/org.apache.dubbo.sample.GreetService/sayHello
在具体实现上,Dubbo Triple 支持 Protobuf Buffer 但并不绑定,比如 Dubbo Java 支持以 Java Interface 定义 Triple 服务,这对于关注特定语言易用性的开发者将更容易上手。另外,Dubbo 当前已经提供了 Java、Go、Rust 等语言实现,目前正在推进 Node.js 等语言的协议实现,我们计划通过多语言和 Triple 协议打通移动端、浏览器、后端微服务体系。
在 Triple 的实现中核心的组件有以下几个:
TripleInvoker 是 Triple 协议的核心组件之一,用于请求调用 Triple 协议的服务端。其中核心方法为 doInvoke,该方法会根据请求类型如 UNARY、BiStream 等,发起不一样类型的请求。如 UNARY 在 SYNC 下即同步阻塞调用,一个请求对应一个响应。BiStream 则是双向通讯,客户端可以持续发送请求,而服务端同样也可以持续推送消息,他们之间通过回调 StreamObserver 组件的方法实现交互。
TripleClientStream 是 Triple 协议的核心组件之一,该组件与 HTTP/2 中的Stream 概念与之对应,每次发起一个新的请求均会创建一个新的TripleClientStream,同理与之对应的 HTTP/2 的 Stream 也是不相同的。TripleClientStream 提供核心的方法有 sendHeader 用来发送头部帧 Header Frame,以及 sendMessage 用来发送数据帧 Data Frame。
WriteQueue 是 Triple 协议中用于写出消息的缓冲队列,其核心逻辑就是将各种操作命令 QueueCommand 添加到内部维护的队列中,并尝试将这些 QueueCommand 对应的任务提交到 Netty 的 EventLoop 线程中单线程、有序的执行。
QueueCommand 是专门用于提交到 WriteQueue 的任务抽象类,不同的 Command 对应了不同的执行逻辑。
TripleServerStream 是 Triple 协议中服务端的 Stream 抽象,该组件与 HTTP/2 中的 Stream 概念与之对应,客户端每通过一个新的 Stream 发起请求,服务端便会创建一个与之对应的 TripleServerStream,以便处理客户端发来的请求信息。
2. HTTP/2
HTTP/2 是一种新一代的 HTTP 协议,是 HTTP/1.1 的替代品,HTTP/2 相较于 HTTP/1.1 的最大改进在于减少了资源的消耗提高了性能。HTTP/1.1 中,浏览器只能在一个 TCP 连接中发送一个请求。如果浏览器需要加载多个资源,那么浏览器就需要建立多个 TCP 连接。这种方式会导致一些问题,例如 TCP 连接的建立和断开会增加网络延迟,而且浏览器可能会在同一时间内发送多个请求导致网络拥塞。
相反,HTTP/2 允许浏览器在一个 TCP 连接中同时发送多个请求,多个请求对应多个 Stream 流,多个流之间相互独立,并以并行的方式流转。而在每个流中,这些请求会被拆分成多个 Frame 帧,这些帧在同一个流中以串行的方式流转,严格的保证了帧的有序性。因此客户端可以并行发送多个请求,而服务器也可以并行发送多个响应,这有助于减少网络连接数,以及网络延迟和提高性能。
HTTP/2 还支持服务器推送,这意味着服务器可以在浏览器请求之前预加载资源。例如,如果服务器知道浏览器将要请求一个特定的资源,那么服务器可以在浏览器请求之前将该资源推送到浏览器。这有助于提高性能,因为浏览器不需要等待资源的请求和响应。
HTTP/2 还支持头部压缩,这意味着 HTTP 头部中的重复信息可以被压缩。这有助于减少网络带宽的使用。
3. Netty
Netty 是一个高性能异步事件驱动的网络框架,主要用于快速开发可维护的高性能协议服务器和客户端。它的主要特点是易于使用、灵活性强、性能高、可扩展性好。Netty 使用 NIO 作为基础,可以轻松地实现异步、非阻塞的网络编程,支持 TCP、UDP、HTTP、SMTP、WebSocket、SSL 等多种协议。Netty 的核心组件包括Channel、EventLoop、ChannelHandler 和 ChannelPipeline。
Channel 是一个传输数据的双向通道,可以用来处理网络 I/O 操作。Netty 的Channel实现了 Java NIO 的 Channel 接口,并在此基础上添加了一些功能,例如支持异步关闭、绑定多个本地地址、绑定多个事件处理器等。
EventLoop 是 Netty 的核心组件之一,它负责处理所有 I/O 事件和任务。一个 EventLoop 可以管理多个 Channel,每个 Channel 都有一个对应的 EventLoop。EventLoop 使用单线程模型来处理事件,避免了线程之间的竞争和锁的使用,从而提高了性能。
ChannelHandler 是连接到 ChannelPipeline 的处理器,它可以处理入站和出站的数据,例如编码、解码、加密、解密等。一个 Channel 可以有多个 ChannelHandler,ChannelPip