# RPC 手册
`rs-zero` 的 `rpc` feature 提供 tonic 相关 helper,用于减少项目重复配置。
## 核心能力
- client endpoint 配置。
- request-id interceptor。
- `grpc-timeout` deadline metadata helper。
- 客户端 retry/backoff helper,默认只重试 `Unavailable`、`DeadlineExceeded`、`ResourceExhausted`。
- memory/static discovery 到 RPC endpoint 的解析适配。
- weighted round-robin endpoint selector。
- streaming wrapper,记录 send/recv、完成状态、timeout 和 tonic code。
- health server helper。
- graceful shutdown 配合 `rs_zero::core::shutdown_signal`。
## 示例
```rust
use rs_zero::core::shutdown_signal;
use rs_zero::rpc::serve_health_with_shutdown;
let addr = "127.0.0.1:50051".parse().unwrap();
serve_health_with_shutdown(addr, shutdown_signal()).await.unwrap();
```
完整示例见 `examples/rpc-hello/src/main.rs`。
## 客户端生产配置
默认 `RpcClientConfig::new` 保持保守行为:不启用 retry、deadline 传播、发现负载均衡或 streaming 观测。需要 go-zero 风格生产默认值时使用 `go_zero_defaults`:
```rust
use rs_zero::rpc::{LoadBalancePolicy, RpcClientConfig};
let config = RpcClientConfig::go_zero_defaults("http://127.0.0.1:50051");
assert!(config.retry.enabled);
assert!(config.deadline.propagate);
assert!(config.streaming.observe);
assert_eq!(config.load_balance.policy, LoadBalancePolicy::WeightedRoundRobin);
```
retry helper 会限制最大尝试次数,并按指数退避执行。需要把重试裁剪到调用总预算时,使用 `DeadlineBudget`:
```rust
use std::time::Duration;
use rs_zero::rpc::{
RpcRetryConfig,
deadline::DeadlineBudget,
retry::run_with_retry_budget,
};
let retry = RpcRetryConfig::go_zero_defaults();
let budget = DeadlineBudget::new(Duration::from_secs(2));
let result = run_with_retry_budget(&retry, &budget, |remaining| async move {
// 将 remaining 传给下游 request timeout 或 grpc-timeout metadata。
Ok::<_, tonic::Status>(())
}).await;
```
## deadline 与 metadata
`deadline_interceptor` 可给 tonic request 注入 `grpc-timeout`,已存在 metadata 时不会覆盖:
```rust
use std::time::Duration;
use rs_zero::rpc::deadline_interceptor;
let interceptor = deadline_interceptor(Duration::from_millis(500));
```
手写 adapter 可直接使用 `insert_grpc_timeout` 和 `remaining_timeout_from_metadata`。
## discovery 与负载均衡
启用 `discovery` feature 后,可把 `Discovery` 实例解析为 tonic endpoint:
```rust
use rs_zero::{
discovery::{InstanceEndpoint, MemoryRegistry, Registry, ServiceInstance},
rpc::{
RpcClientConfig,
balancer::WeightedRoundRobinBalancer,
discovery::resolve_service_endpoint,
endpoint_from_rpc_endpoint,
},
};
let registry = MemoryRegistry::new();
registry.register(
ServiceInstance::new(
"greeter",
"a",
InstanceEndpoint::new("127.0.0.1", 50051)?,
)
.with_weight(2),
).await?;
let selector = WeightedRoundRobinBalancer::new();
let endpoint = resolve_service_endpoint(®istry, &selector, "greeter").await?;
let config = RpcClientConfig::go_zero_defaults(&endpoint.uri);
let tonic_endpoint = endpoint_from_rpc_endpoint(&endpoint, &config)?;
```
当前 selector 保持低基数观测边界:指标和日志只记录 service/method/code,不记录实例 id、IP 或动态 key。
## RPC unary 自动韧性层
`RpcUnaryResilienceLayer` 可挂到 tonic/Tower service,在业务方法未显式调用 `run_unary` 时也能应用 timeout、concurrency、breaker、shedder 和 Redis limiter:
```rust
use rs_zero::rpc::{RpcResilienceLayer, RpcServerConfig, RpcUnaryResilienceLayer};
let config = RpcServerConfig::go_zero_defaults("hello", "127.0.0.1:50051".parse()?);
let resilience = RpcResilienceLayer::new(config.name.clone(), config.resilience.clone());
let layer = RpcUnaryResilienceLayer::new(resilience);
```
生成代码仍保留 `run_unary` helper,便于 skeleton 内直接包裹业务占位。手写 tonic service 推荐使用自动层降低漏接风险。
## streaming 观测边界
`RpcStreamingObserver` 与 `ObservedRecvStream` 是给生成代码或手写 streaming adapter 使用的组合式 wrapper:
```rust
use rs_zero::rpc::{RpcStreamingConfig, streaming::RpcStreamingObserver};
let observer = RpcStreamingObserver::new(
"chat.Chat",
"Talk",
RpcStreamingConfig::go_zero_defaults(),
);
observer.record_send().await;
observer.record_recv().await;
observer.finish::<()>(Ok(())).await;
let snapshot = observer.snapshot().await;
```
`ObservedRecvStream` 可包装 `Stream<Item = Result<T, tonic::Status>>`,`record_stream_send` 可包裹发送动作,`run_observed_stream` 可应用 per-stream timeout。它们不会替代 tonic 生成的 stream 类型;业务代码仍负责处理消息流、取消和 backpressure。
## RPC codegen
`rzcli rpc gen` 提供 proto 子集解析和 tonic skeleton 生成。使用方式见 [RPC 服务教程](../tutorials/rpc-service.md)。
## 限制
- 不替代 `protoc`。
- 生成 skeleton 默认不包含业务实现。
- 复杂 proto 特性需在应用层接入 prost/tonic build。
- 当前负载均衡先提供 weighted round-robin;P2C、channel pool 和连接预热仍需应用层或后续版本扩展。
- streaming wrapper 提供组合式 send/recv/finish/timeout 观察边界,不强行替代所有 tonic stream 类型。
## 相关测试
- `cargo test --test rpc_integration`
- `cargo test --test rpc_production_integration`
- `cargo test -p rs-zero-cli --test goctl_rpc_compat`