1- # 从零开始构建 Rust RPC 框架「一、基本框架 」
1+ # 从零开始构建 Rust RPC 框架「一、星辰大海 」
22
33### 1. 星辰大海
44
@@ -8,11 +8,12 @@ Rust 生态里,RPC 框架的关注度似乎不高,Web 框架倒是有不少
882 . 序列化协议使用 JSON 和 [ MessagePack] ( https://msgpack.org/ ) ,数据包含 schema 能自解释;
993 . 传输层协议支持 TCP、WebSocket 和 RDMA,支持前端页面调用 RPC;
10104 . 高性能、低延迟,优先优化 RDMA 网络场景;
11- 5 . 支持 RPC 回调,server 可以调用 client 提供的方法;
12- 6 . 完善的可观测性,提供充足的 metrics 和 logs,支持分布式 tracing;
13- 7 . 丰富的预定义 service,例如查询 server stats,开箱可用;
14- 8 . [ 可选] 支持网页 API 文档和接口调试工具,支持 JSON Schema;
15- 9 . [ 可选] 支持 Python 接口绑定。
11+ 5 . 支持反向 RPC,server 可以调用 client 提供的方法;
12+ 6 . 支持反射,server 能否向 client 暴露和描述自身所提供的服务和方法;
13+ 7 . 完善的可观测性,提供充足的 metrics 和 logs,支持分布式 tracing;
14+ 8 . 丰富的预定义 service,例如查询 server stats,开箱可用;
15+ 9 . [ 可选] 支持网页 API 文档和接口调试工具,支持 JSON Schema;
16+ 10 . [ 可选] 支持 Python 接口绑定。
1617
1718以上这些目标我不确定能否都实现。我会在这个系列的博文里详细介绍我的想法和设计。为了避免烂尾,这个系列的博文我会设置为隐藏,等主要目标都实现了我再一次性公开。
1819
@@ -82,7 +83,7 @@ impl EchoService for DemoImpl {
8283}
8384```
8485
85- 当 Server 端收到一条包含调用信息和参数序列化结果的请求时,如何调用对应的方法呢?这里还需要生成一个方法名到调用函数的映射。ruapc 中基于 tokio 和 [ return type notation] ( https://rust-lang.github.io/rfcs/3654-return-type-notation.html ) ,使用宏为 ` trait ` 生成 ` ruapc_export ` 方法 :
86+ 当 Server 端收到一条包含调用信息和参数序列化结果的请求时,如何调用对应的方法呢?这里还需要生成一个方法名到调用函数的映射。ruapc 中基于 tokio 和 [ return type notation] ( https://rust-lang.github.io/rfcs/3654-return-type-notation.html ) ,使用宏为 ` trait ` 生成 ` ruapc_export ` 方法来实现 dispatch :
8687
8788``` rust
8889pub type Method = Box <dyn Fn (Context , RecvMsg ) -> Result <()> + Send + Sync >;
@@ -95,7 +96,7 @@ pub trait EchoService {
9596 ) -> :: std :: collections :: HashMap <String , ruapc :: Method >
9697 where
9798 Self : 'static + Send + Sync ,
98- Self (.. ): Send ,
99+ Self :: echo (.. ): Send ,
99100 {
100101 let mut map = :: std :: collections :: HashMap :: <String , ruapc :: Method >:: default ();
101102 let this = self . clone ();
@@ -128,11 +129,10 @@ pub trait EchoService {
128129
1291301 . 我并不希望宏生成任何新的 ` struct ` 类型;
1301312 . 我喜欢所见即所得,我并不希望宏生成的函数篡改了函数声明;
131- 3 . 我希望在 client 调用时,保持完全一致函数签名,所有要求返回值一定是一个 ` Result<T, E> ` ,并且要求 ` ruapc::Error ` 可以转为用户声明的 ` Error ` 类型;
132- 4 . Rust 目前(2024 edition,1.88)对 async trait dynamic dispatch 的支持依然不够好。可以依赖 [ dynosaur] ( https://crates.io/crates/dynosaur ) 实现,但我觉得还不够好用;
133- 5 . 最终使用 ` ruapc_export ` 将所有异步方法导出为同步方法 + ` tokio::spawn ` ,是我能想到的可行且能接受的唯一方案了。与 tokio 强绑定对我来说并不是一个问题,毕竟它基本也是事实标准;
134- 6 . 使用 return type notation 是为了满足 ` tokio::spawn ` 的需求。该特性目前还不是稳定的状态,所以整个项目都必须依赖 nightly 版本的 Rust。这对我来说不是一个问题,不过还是希望在 ruapc 完成前这个特性可以 Stable;
135- 7 . 只有确定了具体的方法,才能对参数进行对应类型的反序列化。
132+ 3 . 我希望在 client 调用时,保持完全一致函数签名。因为 RPC 一定是有概率失败的,所有要求返回值一定是一个 ` Result<T, E> ` ,并且要求 ` ruapc::Error ` 可以转为用户声明的 ` Error ` 类型;
133+ 4 . 我希望拿到 context 时,client 就可以发起调用,在 server 端处理请求时获得的 context 亦可发起调用,也就能实现非常直觉的反向 RPC;
134+ 5 . Rust 目前(2024 edition,1.88)对 async trait dynamic dispatch 的支持依然不够好。可以依赖 [ dynosaur] ( https://crates.io/crates/dynosaur ) 实现,但我觉得还不够好用。最终使用 ` ruapc_export ` 将所有异步方法导出为同步方法 + ` tokio::spawn ` ,是我能想到的可行且能接受的方案了。与 tokio 强绑定对我来说并不是一个问题,毕竟它基本也是事实标准;
135+ 6 . 使用 return type notation 是为了满足 ` tokio::spawn ` 的需求。该特性目前还不是稳定的状态,所以整个项目都必须依赖 nightly 版本的 Rust。这对我来说不是一个问题,不过还是希望在 ruapc 完成前这个特性可以稳定下来。
136136
137137在 Client 端,我们希望可以直接调用 ` EchoService::echo ` 方法触发 RPC 请求。也就是说 ` Client ` 端需要有一个默认的 ` EchoService ` 实现,它实际上会完成序列化、发送、接收、反序列化的步骤。我们使用 Rust 的宏实现这一目的,详情见[ 代码] ( https://github.com/SF-Zhou/ruapc/blob/b64248314de3eacfcbf2d6ab1f3ec5f7ad6a3edf/ruapc-macro/src/lib.rs ) ,宏会帮我生成如下的代码:
138138
@@ -144,7 +144,7 @@ impl EchoService for ruapc::Client {
144144}
145145```
146146
147- 生成的代码将方法名、请求类型和返回类型(依赖推导 )传递给 ` ruapc_request ` 方法,在该方法中完成 RPC 请求:
147+ 生成的代码将方法名、请求类型和返回类型(依赖类型推导 )传递给 ` ruapc_request ` 方法,在该方法中完成 RPC 请求:
148148
149149``` rust
150150impl Client {
@@ -216,7 +216,7 @@ pub struct MsgMeta {
2162164 . ` MsgMeta ` 序列化结果,长度为 ` meta_len `
2172175 . 参数序列化结果,长度为 ` total_len ` - ` meta_len ` - 4,4 为大端 ` u32 ` 的长度
218218
219- 按照上述规则实现序列化的逻辑,代码参考[ socket.rs 文件 ] ( https://github.com/SF-Zhou/ruapc/blob/b64248314de3eacfcbf2d6ab1f3ec5f7ad6a3edf/ruapc/src/socket.rs#L26-L105 ) 和 [ msg.rs 文件 ] ( https://github.com/SF-Zhou/ruapc/blob/b64248314de3eacfcbf2d6ab1f3ec5f7ad6a3edf/ruapc/src/msg.rs#L40-L69 ) 。
219+ 按照上述规则实现序列化的逻辑,代码参考[ socket.rs] ( https://github.com/SF-Zhou/ruapc/blob/b64248314de3eacfcbf2d6ab1f3ec5f7ad6a3edf/ruapc/src/socket.rs#L26-L105 ) 文件和 [ msg.rs] ( https://github.com/SF-Zhou/ruapc/blob/b64248314de3eacfcbf2d6ab1f3ec5f7ad6a3edf/ruapc/src/msg.rs#L40-L69 ) 文件 。
220220
221221### 5. 连接管理
222222
0 commit comments