use service::stub::CalculatorService;
use tcpserver::RpcServer;
fn main() -> Result<(), service::error::RpcError> {
let mut server = RpcServer::bind("127.0.0.1:3000")?;
server.register_service("calculator", CalculatorService);
let server = server.worker_threads(4);
// server.require_secure() // 可选:只接受安全连接
server.run()
}
服务端用法(基础并发)
use service::stub::CalculatorService;
use tcpserver::RpcServer;
fn main() -> Result<(), service::error::RpcError> {
let mut server = RpcServer::bind("127.0.0.1:3000")?;
server.register_service("calculator", CalculatorService);
let server = server.worker_threads(4); // 线程池模型;不调用则每连接一个线程
server.run()
}
客户端用法
use serde_json::json;
use tcpclient::RpcClient;
fn main() -> Result<(), service::error::RpcError> {
let mut client = RpcClient::connect("127.0.0.1:3000")?;
// 或安全模式:RpcClient::connect_secure("127.0.0.1:3000")?
let result = client.call("calculator", "add", json!([1, 2]))?; // 3
let result = client.call("calculator", "multiply", json!([3, 4]))?; // 12
Ok(())
}
[server] RPC server listening on 127.0.0.1:3000 (services: calculator)
[server] concurrency: fixed thread pool, 4 workers
[server] security: auto (plaintext or secure handshake)
Rust-RPC
基于 Rust 标准库
TcpListener/TcpStream和 JSON 的自研 RPC 框架。没有使用任何现成 RPC 框架(无
tonic/tarpc/jsonrpsee,也不依赖任何异步运行时): Socket 编程、自定义帧协议、JSON 消息、服务注册与分发、线程池、混合加密握手、 IDL 编译器全部自行实现;第三方依赖仅限通用基础库(serde/serde_json 序列化、 rsa/sha2/rand 密码学原语、aes-gcm 对称加密)。 当前版本:**v0.8 最终版(跨语言客户端、性能测试与完整文档)**。实现功能对应关系
TcpListener/TcpStream,自定义帧协议service/src/frame.rsCargo.tomltcpclient/tcpserverservice/src/stub.rsservice/src/registry.rs、tests/platform.rsread_exact/write_all,半包/粘包/超长/非法帧处理service/src/frame.rs、tests/framing.rsworker_threads(N))tcpserver/src/thread_pool.rsservice/src/security.rs、crypto/.rpc文件四阶段编译,生成类型安全 Rust 客户端rpc-idl/clients/python/DESIGN.md/PROTOCOL.md/TESTING.md/benchmarks/版本演进记录
Calculator.add(1,2) → 3RPC1长度前缀帧解决半包/粘包;request_id;结构化错误码RpcServicetrait + 注册表动态分发,四方法,平台化验收RpcServer/RpcClient,全部接口Result<T, RpcError>.rpc四阶段编译器,生成类型安全客户端,确定性输出每个版本有独立 Git 标签(
git tag -l查看),提交历史保持每步可编译可测试。当前版本:v0.8-final-cross-language
最终提交版本,在 v0.7 基础上完成跨语言加分项、性能数据与完整文档:
cargo run -p tcpclient进入rpc>提示符, 自定义服务/方法/参数发起调用(参数按 JSON 解析,支持引号字符串), 也可命令行单次调用(cargo run -p tcpclient -- calculator add 1 2);help查看用法,quit退出,--secure同样适用;clients/python/,纯标准库):实现与 Rust 完全相同的 帧协议,调用calculator.add(1, 2)、calculator.multiply(3, 4)成功, 证明协议没有绑定 Rust 内部类型;业务错误同样以结构化错误码返回;benchmarks/+tcpclient/examples/bench_client.rs): 串行(明文/安全 × 小/4KB 消息 × 连接复用/每次重连)与并发 (1/4/8/16 客户端)实测数据,全部由真实运行产生并附复现命令; 要点:明文小消息 58µs/次(17k calls/s),8 客户端并发吞吐 39k calls/s, 安全模式每次重连需 ~263ms(临时 RSA 密钥对生成)——连接复用价值一目了然;RPC_QUIET=1静默日志开关(基准测试使用,不影响正常演示);IDL 与代码生成
轻量 IDL:业务接口用
.rpc文件描述,自研编译器(词法 → 语法 → 语义 → 生成, 纯标准库)生成类型安全的 Rust 客户端包装;语义检查覆盖未知类型、重复命名、 Rust 关键字冲突等,生成确定性输出可用 diff 验证。生成代码包装RpcClient, 明文与安全连接均可直接使用。支持类型:
i32/string/bool;//行注释。语法定义
示例
examples/calculator.rpc:编译命令
输出的
generated/calculator_client.rs(节选):与手写客户端的对比
手写调用需要拼服务名/方法名字符串、手工构造
json!参数、拿到Value后自行断言类型:生成代码把这三件事全部固化:方法名与服务名来自 IDL,参数直接用原生类型, 返回值按声明类型解码——调用方写
client.add(1, 2)即可,接口变更时重新编译 IDL 即暴露所有不匹配,而不是运行时才发现。自动生成代码的演示
启动临时服务端(注册
Calculator)并用生成的CalculatorClient完成四次调用。 端到端验收见tcpclient/tests/codegen.rs(含安全连接与错误透传场景)。安全传输
设计要点:
invalid_frame错误帧并断开连接,绝不 panic;服务端用法(并发 + 安全)
服务端用法(基础并发)
客户端用法
服务端返回业务错误(服务/方法不存在、参数错误等)时,
call返回携带对应错误码的Err(RpcError);传输层失败映射为传输错误码。并发模型
不调用
worker_threads时退化为”每连接一个线程”模型:accept 后直接thread::spawn,实现最简单但线程数随客户端增长。并发测试数据(实测)
环境:i7-1165G7(4 核 8 线程)/ 40 GB / Windows 10 / rustc 1.98.0 / debug 构建 / 回环地址。
(回环 + 简单加法负载下加速比达不到理论 4×:客户端线程本身、accept 与调度共享同一台机器。)
传输协议:自定义帧格式
TCP 是字节流协议,一次
read可能读到半条消息或多条消息。 所有请求/响应以长度前缀帧传输:write_all先写满 9 字节头,再写满 payload,长度由序列化结果计算;read_exact先读满头部,校验 magic/版本/长度后,再read_exact读满 payload;read_exact的”读不满就等”解决,粘包由”每次恰好消费一帧字节数”解决;DEFAULT_MAX_FRAME_SIZE),超限在读 payload 之前拒绝;invalid_frame错误帧后断开连接。消息格式
请求(帧 payload):
响应(帧 payload):
出错时:
request_id由客户端生成,响应原样带回,用于请求/响应配对。 未知请求字段被忽略(向前兼容)。错误码表
invalid_frameinvalid_requestdecode_errorservice_not_foundmethod_not_foundinvalid_paramsinternal_errorconnection_failedconnection_closedwrite_failedread_failedsecure_mode_required内置服务
Calculator(
service/src/stub.rs),参数均为[a: i32, b: i32]:add[1, 2]3subtract[5, 3]2multiply[4, 6]24divide[8, 2]4(b=0时返回invalid_params)平台化:如何新增一个服务
新增服务只需要两步,不触碰传输层与服务端连接处理:
RpcServicetrait(参考service/src/stub.rs的CalculatorService):tcpserver/src/main.rs启动时注册:客户端只需把请求中的
service/method/params换成对应值即可调用。tcpserver/tests/platform.rs中有一个只存在于测试代码里的StringService, 它未改动任何运行时代码就被远程调用成功,是这一特性的直接验收证据。目录结构
编译与运行
要求:Rust 1.70+(在
rustc 1.98.0 / x86_64-pc-windows-msvc上验证通过)。演示(需要两个终端,先启动服务端):
跨语言调用(Python 3.8+,纯标准库,服务端不关):
IDL 生成客户端与性能基准:
(性能数据与矩阵见 benchmarks/;基准运行时 服务端日志自动静默,或手动设置环境变量
RPC_QUIET=1。)服务端输出:
客户端输出(交互模式,在
rpc>提示符下自定义服务/方法/参数;参数按 JSON 解析, 数字与 true/false 直接输入,字符串可加双引号):测试
service单元测试 32 个:错误码序列化(含传输层错误码)、帧字节布局、 字节级分片读取(逐字节喂入)、连续帧不混淆、非法 magic/版本/超长拒绝、截断报错、 注册表分发/覆盖/未知服务、Calculator 四方法与各类参数错误;tcpserver/tests/loopback.rs8 个:四方法调用、同一连接连续调用、request_id 回带、 未知服务/方法、参数错误、除零、非法 JSON 的错误码断言;tcpserver/tests/framing.rs7 个:真实 TCP 上的半包、粘包(两帧连写)、大于 1KB payload、 非法 magic 拒绝并断开、超长帧在读 payload 前拒绝、非法 JSON 返回结构化错误且连接可继续用、 同一连接连续 100 次调用;tcpserver/tests/platform.rs4 个:测试内自定义服务(未改动运行时)可被远程调用、 与内置服务共存、自定义服务的方法/参数错误结构化、未注册服务返回 service_not_found;tcpclient/tests/runtime.rs4 个(公共 API 端到端):同一 RpcClient 连续四次调用 不重建连接、业务错误透传为对应错误码、连接失败 → connection_failed、 服务端中途断开 → connection_closed;tcpclient/tests/concurrency.rs3 个 + 1 个默认忽略的吞吐基准: 10 客户端并发各 100 次调用全部正确、共享状态计数服务 1000 次并发自增无丢失无重复、 4 worker 并行处理 4 个 sleep 请求(306ms,串行下限 1200ms); 基准bench_single_worker_vs_four_workers用cargo test -p tcpclient --test concurrency -- --ignored --nocapture运行;tcpclient/tests/secure.rs6 个(安全传输验收):安全模式四方法调用且共享同一 安全上下文、明文与安全客户端共存于同一服务端、线上字节是密文(不能按 JSON 解析, 解密后可读)、4KB 大消息经安全通道传输、篡改密文收到结构化错误并被断开、require_secure服务端拒绝明文(secure_mode_required)但接受安全连接;tcpclient/tests/codegen.rs4 个(生成代码验收):生成客户端四方法调用、 业务错误透传、服务名不匹配 → service_not_found、生成客户端在安全连接上工作;rpc-idl32 个:词法(记号流/注释/定位/非法字符)、语法(完整解析/零参数方法/ 多服务/缺分号缺 returns 报错)、语义(未知类型/重复方法/重复服务/Rust 关键字/ 空服务/参数上限/多错误汇总)、生成(命名/内容/按需解码器/确定性输出)、 流水线集成(位置定位/错误收集/多服务多文件);crypto单元测试 6 个:密钥生成、OAEP 往返、DER 往返、非法 DER、 错误密钥解密、篡改密文拒绝;service/src/security.rs单元测试 8 个:明文恒等、AES 小/大(100KB)消息往返、 密文非明文且非合法 JSON、随机 nonce(相同明文密文不同)、篡改/错误密钥/截断拒绝。