- 后端
- RPC框架
【免费下载链接】grpc-rust
A native gRPC client & server implementation with async/await support.
本指南聚焦当前仓库中grpc-benchmark目录下的 gRPC 基准测试框架代码,完整剖析其在 Rust 生态中落地 gRPC 官方基准测试协议的方式:包括 worker 进程的启动方式、BenchmarkServer 与 BenchmarkClient 的压测实现、直方图延迟统计原理,以及独立命令行工具bench的使用方法。读完本文,你将掌握这套框架的目录结构、控制协议(control.proto)中各个配置字段的含义、压测数据的采集与上报机制,并能直接编译运行 worker 与 bench 对本地 gRPC 服务发起基准测试。
框架定位:gRPC 官方基准测试体系中的 Rust 实现
grpc-benchmark/README.md 明确指出:该目录承载的是gRPC Benchmarking Framework(gRPC 基准测试框架)中 worker、server、client 三部分的 Rust 实现代码。框架的整体设计遵循 gRPC 官方的基准测试约定:
- Driver(驱动):负责编排整个测试场景(如并发数、RPC 类型、测试时长),向各 worker 下发配置并汇总结果;官方驱动代码及运行说明位于 gRPC 官方仓库的
tools/run_tests/performance目录下,不在本仓库内; - Worker(工作进程):由 driver 通过 gRPC 远程调用控制,负责在本机启动压测 server 或 client;
- BenchmarkServer / BenchmarkClient:真正执行负载与统计的实体;
- 持续监控:官方通过这套基准测试持续跟踪各语言 gRPC 实现的性能,指标数据汇总到 gRPC 官方性能仪表盘(README 中提到的 performance dashboard)。
需要说明的是:本仓库内虽然没有官方 driver 的代码,但提供了一个自包含的独立驱动工具bench(源码见 grpc-benchmark/src/bin/bench.rs),它可以本地拉起 worker、下发配置、运行压测并直接打印报告,便于在无法连接官方 driver 的情况下快速验证框架本身。这也从侧面印证了 worker/server/client 三层架构在 Cargo.toml 中的落地:该 crate 声明了两个二进制目标bench与worker,并导出了库模块供二者复用。
目录结构与模块总览
grpc-benchmark的核心布局如下(以下路径均以仓库根目录为起点):
| 路径 | 职责 |
|---|---|
| grpc-benchmark/src/main.rs | worker二进制入口:解析--driver_port并启动 WorkerService gRPC 服务 |
| grpc-benchmark/src/bin/bench.rs | bench二进制入口:自包含驱动,解析压测参数并驱动 worker |
| grpc-benchmark/src/worker.rs | WorkerService实现:暴露run_server、run_client、core_count、quit_worker四个 RPC |
| grpc-benchmark/src/server.rs | BenchmarkServer与被测BenchmarkService(proto 服务器)实现 |
| grpc-benchmark/src/client.rs | BenchmarkClient:闭循环压测负载生成与延迟直方图采集 |
| grpc-benchmark/src/rusage.rs | 跨平台 CPU 用户态/内核态时间采集封装 |
| grpc-benchmark/src/lib.rs | 汇总生成代码与模块,同时编译 protobuf 与 tonic 两套生成产物 |
| grpc-benchmark/proto/grpc/testing/ | 官方基准测试协议定义:control.proto、benchmark_service.proto、stats.proto、payloads.proto、messages.proto、worker_service.proto等 |
| grpc-benchmark/data/tls/ | 基准测试专用 TLS 测试证书(ca.pem、server1.pem、server1.key) |
lib.rs中generated模块同时 include 了两套代码生成产物:一套是基于 protobuf 运行时(protoc-gen-rust-grpc风格,模块路径generated::grpc::testing),一套是基于 tonic 与 prost(generated::services::grpc::testing)。前者用于驱动脚本bench构造配置消息,后者用于实现真正的 gRPC 服务与客户端。build 阶段的代码生成由grpc-protobuf-build、protoc-gen-rust-grpc与tonic-prost-build三个本地 crate 完成(见 Cargo.toml 的 build-dependencies)。
worker:承载压测的 gRPC 服务进程
启动方式与命令行参数
worker是一个独立的可执行文件,其唯一必需参数是 driver 通信端口,用法如下:
worker --driver_port=<port>从 grpc-benchmark/src/main.rs 的解析逻辑可以看到:入口会遍历std::env::args(),仅接受形如--driver_port=<port>的参数,port必须是合法的u16端口号,否则打印错误并退出;其他未知参数会打印Warning: Unrecognized argument。未提供--driver_port时输出Usage: worker --driver_port=<port>并以非零码退出。该文件注释还解释了线程模型的一个设计取舍:默认 Tokio 运行时每个逻辑处理器一个线程,官方测试框架虽然支持在测试配置中指定线程数,但部署在 k8s 上的测试依赖特定机型规格、不期望 client/server 自行限制资源占用,且 Tokio 不支持嵌套运行时,因此当前版本未实现按测试配置设定线程数的能力。
WorkerService 的四个 RPC
worker进程通过tonic启动一个 gRPC 服务,将WorkerServiceServer::new(WorkerServer::new(quit_notify))挂载到0.0.0.0:<driver_port>,并使用serve_with_shutdown让quit_workerRPC 触发优雅退出(见 grpc-benchmark/src/main.rs 的run_worker)。WorkerService定义于官方协议文件 grpc-benchmark/proto/grpc/testing/worker_service.proto,其 Rust 实现位于 grpc-benchmark/src/worker.rs:
run_server/run_client:均为服务端流式 RPC。driver 通过流式请求先下发Setup(对应ServerConfig/ClientConfig),之后周期性下发Mark(reset为 true 表示快照后清零统计)来取回统计数据。worker 端使用async_stream::try_stream!逐个处理请求,并禁止重复启动(already_exists错误)。统计通过闭包yield回ServerStatus/ClientStatus,其中ServerStatus还携带绑定的端口号与可用 CPU 核数;core_count:一元 RPC,返回available_parallelism()探测到的逻辑核数;quit_worker:一元 RPC,触发Notify通知serve_with_shutdown正常退出。
run_server的实现细节(grpc-benchmark/src/worker.rs 的run_server分支)体现了ServerStatus的字段来源:stats来自BenchmarkServer::get_stats,cores来自core_count(),port来自server.port()——其中端口是启动时实际绑定到的端口(配置为 0 时由系统分配)。run_client分支则要求 client 存在时才能响应Mark,并在每个请求处理后异步取回ClientStats。
控制协议:配置字段与语义
所有压测配置都定义在官方控制协议 grpc-benchmark/proto/grpc/testing/control.proto 中,理解这些字段是正确使用框架的前提:
ClientConfig 关键字段
server_targets:待连接的目标列表(至少一个);worker 端实际构造连接时会在每个 target 前补dns:///前缀(见 grpc-benchmark/src/client.rs);client_type:SYNC_CLIENT、ASYNC_CLIENT等枚举;当前 Rust 实现接受SyncClient与AsyncClient两种取值;outstanding_rpcs_per_channel:每个 channel 上并发的在途 RPC 数(必须 > 0);client_channels:要创建的独立 channel 数(必须 > 0),第 i 个 channel 连接server_target[i % size];rpc_type:UNARY/STREAMING(服务端流式 ping-pong)等枚举;load_params:负载模型。ClosedLoopParams(一个 RPC 完成后立即发起下一个)是当前实现的唯一支持项;PoissonParams(泊松到达率的开放环路)在源码中显式返回unimplemented,注释说明待 xDS 基准测试支持时再实现;payload_config:负载内容。支持SimpleProtoParams(req_size/resp_size);ByteBufferParams与ComplexProtoParams在 client 与 server 中均返回unimplemented;histogram_params:延迟直方图参数(resolution与max_possible,语义见下文)。
ServerConfig 与安全参数
ServerConfig的关键字段是port(0 表示由系统挑选空闲端口,实际绑定值通过ServerStatus.port回传给驱动)与security_params。SecurityParams中use_test_ca为 true 时启用 TLS 并使用内置测试证书;server_host_override用于客户端覆盖连接 authority(SNI/证书校验主机名)。
负载与基准服务定义
被测服务定义于 grpc-benchmark/proto/grpc/testing/benchmark_service.proto,即BenchmarkService:
UnaryCall(SimpleRequest) returns (SimpleResponse):一请求一响应,服务器原样回显客户端负载;StreamingCall(stream SimpleRequest) returns (stream SimpleResponse):流式 ping-pong,逐条回显;StreamingFromClient/StreamingFromServer/StreamingBothWays:其余三种组合形态,本仓库的 proto server 均返回unimplemented(见 grpc-benchmark/src/server.rs 中ProtoServer的实现)。
BenchmarkServer:被压测服务的实现要点
BenchmarkServer::start(grpc-benchmark/src/server.rs)负责按ServerConfig拉起被压测服务,核心要点如下:
- HTTP/2 自适应窗口:
Server::builder().http2_adaptive_window(Some(true))开启自适应流控窗口,这是基准测试场景下提升高带宽流吞吐的常见调优项; - TLS 支持:当配置了
SecurityParams且use_test_ca为 true 时,加载编译期嵌入的测试证书(include_bytes!读取 grpc-benchmark/data/tls/server1.pem 与server1.key)构造ServerTlsConfig;否则使用空的 TLS 配置; - 动态端口:
port = 0时绑定到0.0.0.0:0,由操作系统分配端口,随后通过local_addr()获取实际端口并保存到BenchmarkServer.port; - TCP 细节:对每个接入连接调用
set_nodelay(true)关闭 Nagle 算法,降低小包延迟; - 统计采样:
get_stats(reset)基于Instant与Rusage(见 grpc-benchmark/src/rusage.rs,Unix 下用nix的getrusage(RUSAGE_SELF)取用户态与内核态时间差)计算time_elapsed、time_user、time_system,并返回ServerStats;cq_poll_count等字段按官方约定置 0(Java 与 Go 同样不设置这些字段)。
被测 handler 的实现非常轻量:unary_call构造一个响应体为vec![0; response_size]的SimpleResponse直接返回;streaming_call对入站流逐条回显同样大小的响应体,模拟真实协议开销但不去做业务计算,从而让压测聚焦在 RPC 框架本身的吞吐与延迟上。
BenchmarkClient:闭循环压测与直方图采集
BenchmarkClient::start(grpc-benchmark/src/client.rs)是压测负载的生成端,实现要点如下:
- 配置校验:依次校验
client_type、payload_config(仅接受SimpleParams)、load_params(仅接受ClosedLoop)、client_channels > 0、outstanding_rpcs_per_channel > 0、server_targets非空;不合规即返回带具体原因的invalid_argument/unimplemented状态; - TLS 客户端凭证:若配置
use_test_ca,加载内置 grpc-benchmark/data/tls/ca.pem 作为信任根构造RustlsChannelCredentials;若提供server_host_override,则通过Channel::builder(...).authority(...)覆盖连接 authority(测试场景中常用假域名foo.test.google.fr配合测试 CA 做证书校验);否则使用LocalChannelCredentials(明文连接); - 任务拓扑:任务总数
num_tasks = client_channels × outstanding_rpcs_per_channel;每个 channel 一个Channel,按rpc_type为每个在途 RPC spawn 一个 Tokio 任务,运行blocking_unary或blocking_streaming的闭循环; - 闭循环语义:每个任务循环执行——先检查取消标记与统计请求标记(
watch通道),再发起一次 RPC 并计时,将耗时(纳秒)记录进共享的hdrhistogram::Histogram;统计请求到达时将直方图快照通过mpsc通道送回,reset为 true 时清空直方图后继续。流式模式下是"发送一条请求 → 接收一条响应"的 ping-pong,时间从发送前计时到接收完成; - 统计聚合:
get_stats通过watch::Sender广播统计请求,等待所有num_tasks个直方图回传并合并(2 秒超时返回deadline_exceeded),随后基于合并结果计算min_seen、max_seen、sum、sum_of_squares、count,并按指数桶把频次铺满到max_possible上限(驱动端期望覆盖全部桶区间),封装为ClientStats返回。
直方图与延迟百分位计算
延迟统计依赖HistogramParams的两个参数(grpc-benchmark/proto/grpc/testing/stats.proto):
resolution:第一个桶为[0, 1 + resolution),后续桶按resolution指数增长,默认 0.01(即 1%);max_possible:覆盖的最大延迟上限,超过该值histogram.record会失败并打印告警(bench 中设为60e9纳秒,即 60 秒)。
bench通过calculate_percentile(grpc-benchmark/src/bin/bench.rs)在HistogramDataView上做线性插值:累计桶频次直至达到percentile × count,再在目标桶内按比例内插出延迟值,从而得到 p50 / p90 / p99 等指标。
独立驱动 bench:无需官方 driver 的本地压测
bench是理解整套框架调用关系的最佳入口(grpc-benchmark/src/bin/bench.rs)。它展示了"驱动 → worker → 被测服务/客户端"的完整闭环,运行流程为:
- 在
127.0.0.1:0上临时绑定一个监听端口作为 worker 服务端口,本地 spawnWorkerServiceServer; - 用
grpccrate 的Channel(dns:///127.0.0.1:<port>+LocalChannelCredentials)创建WorkerServiceClient; - 通过
run_server流式 RPC 下发ServerConfig{port: 0}(动态端口),等待ServerStatus返回真实绑定端口; - 通过
run_client流式 RPC 下发ClientConfig,等待客户端就绪; - 先预热 5 秒,随后发送
Mark{reset: true}清零统计,再运行设定的时长; - 发送
Mark{reset: false}取回最终ClientStats,计算并打印报告; - 关闭两个流并通知本地 worker 退出。
命令行参数
从--help输出(grpc-benchmark/src/bin/bench.rs 的main函数)可以拿到完整的参数清单:
Usage: bench --duration <secs> --channels <num> --rpcs-per-channel <num> --rpc-type <unary|streaming> [--secure] [--req-size <bytes>] [--resp-size <bytes>]| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
-d, --duration | 是 | 无 | 压测时长(秒) |
--channels | 否 | 1 | 客户端 channel 数 |
--rpcs-per-channel | 是 | 无 | 每个 channel 的并发在途 RPC 数 |
--rpc-type | 是 | 无 | unary或streaming,其他取值报错 |
--secure | 否 | 关闭 | 启用 TLS(使用内置测试 CA) |
--req-size | 否 | 1 | 请求负载字节数 |
--resp-size | 否 | 1 | 响应负载字节数 |
参数解析使用pico-args,多参数字段解析完成后会调用pargs.finish(),任何未消费的残留参数都会被当作错误抛出。启用--secure时,bench 会在ServerConfig中设置SecurityParams{use_test_ca: true},并在ClientConfig中设置SecurityParams{use_test_ca: true, server_host_override: "foo.test.google.fr"}——这正是上面提到的测试 CA + 主机名覆盖的典型用法。
报告输出示例
压测结束后 bench 按如下格式打印结果(QPS 由count / time_elapsed计算,延迟单位毫秒,来自calculate_percentile的插值结果):
Benchmark Config: Duration: 10 s Channels: 1 RPCs per channel: 16 RPC type: unary Security: insecure Request size: 100 bytes Response size: 100 bytes Benchmark server bound to port: 49821 Warming up for 5 seconds... Running benchmark for 10 seconds... Report: QPS: 152301.25 Avg Latency: 0.1037 ms 50th Percentile Latency: 0.0923 ms 90th Percentile Latency: 0.1310 ms 99th Percentile Latency: 0.2846 ms编译与运行
在仓库根目录(工作区)下,可以分别运行两个二进制:
# 方式一:worker 模式(供官方 driver 或自定义驱动远程调用) cargo run -p grpc-benchmark --bin worker -- --driver_port=50051 # 方式二:bench 模式(自包含驱动,本地完成全流程压测) cargo run -p grpc-benchmark --bin bench -- \ --duration 10 --channels 2 --rpcs-per-channel 8 \ --rpc-type unary --req-size 100 --resp-size 100 # TLS 模式 cargo run -p grpc-benchmark --bin bench -- \ --duration 10 --channels 1 --rpcs-per-channel 4 \ --rpc-type streaming --secure编译该 crate 需要仓库内多个本地依赖(grpc、tonic、tonic-prost、grpc-protobuf、protoc-gen-rust-grpc、grpc-protobuf-build、tonic-prost-build等),且 Unix 平台下额外引入nix(resourcefeature)用于资源统计;tonic依赖启用了tls-aws-lc特性(main 入口中通过rustls::crypto::aws_lc_rs::default_provider().install_default()安装默认加密提供者)。需要说明的是:当前实现聚焦于官方基准测试框架的协议兼容,ByteBufferParams、ComplexProtoParams、泊松负载与其余三种流式 RPC 形态在源码中明确标注为未实现,使用时应以SimpleProtoParams+UnaryCall/StreamingCall场景为准。
与官方框架的关系及适用前提
需要再次强调的是本目录在 gRPC 生态中的定位(依据 grpc-benchmark/README.md 的原文表述):本目录只包含 worker、server、client 三个环节的实现,驱动编排代码与完整的运行指导位于 gRPC 官方仓库的tools/run_tests/performance目录,属于外部独立维护的部分;官方通过这些基准测试持续监控各语言 gRPC 实现的性能,并将指标呈现于其性能仪表盘。因此,本文所讲述的worker二进制面向的是官方 driver 下发配置的交互协议,而bench则是本仓库为便于本地验证而提供的独立入口。理解这一分工,就能清楚区分"协议实现"(本仓库的 Rust 部分)与"场景编排"(官方 driver)的边界,也能更准确地解读control.proto中Scenario、ScenarioResultSummary等由 driver 侧消费的数据结构。
- 后端
- RPC框架
【免费下载链接】grpc-rust
A native gRPC client & server implementation with async/await support.
相关推荐
终极指南:gRPC性能基准测试与主流RPC框架全面对比分析
终极指南:gRPC性能基准测试与主流RPC框架全面对比分析 gRPC作为现代云原生应用的远程过程调用(RPC)框架,以其跨语言支持、高效二进制协议和强大的生态系
后端RPC框架微服务通信2025实测:Egg.js性能碾压Express?三大Node.js框架基准测试全解析
2025实测:Egg.js性能碾压Express?三大Node.js框架基准测试全解析 你还在为Node.js后端框架选型纠结?当Express的轻量、Nest
后端Web框架Node.js版本管理实战指南:nvm深度解析与最佳实践
Node.js版本管理实战指南:nvm深度解析与最佳实践 Node.js版本管理是每个JavaScript开发者必须掌握的核心技能之一。nvm(Node Ver
CLI开发工具版本控制
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考