Rivet API 数据模型解析:DatacenterHealth 与数据中心健康度探测(Fanout)
【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors
本篇技术指南以 Rivet 开源仓库中 DatacenterHealth 模型文档 为核心,完整讲解该模型的字段结构、序列化规则,以及它背后的GET /health/fanout健康度探测接口的实现原理。读完本文,你将掌握 Rivet 多数据中心场景下健康状态的数据组织方式,能够正确解析各 SDK 中返回的 DatacenterHealth 数据,并理解服务端是如何并发探测各数据中心并汇总结果的。
一、模型定位:一次健康探测的“单数据中心结果”
在 Rivet 的公开 API(rivet-api-public)中,DatacenterHealth并不是一个独立接口的返回体,而是GET /health/fanout接口返回的 HealthFanoutResponse 中datacenters数组的单个元素类型。它的职责是描述"对某一个具体数据中心的健康检查结果",包括该数据中心的标识、健康状态、往返延迟(RTT)以及可选的详细响应或错误信息。
从服务端源码看,该结构在 engine/packages/api-public/src/health.rs 中定义如下:
#[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct DatacenterHealth { pub datacenter_label: u16, pub datacenter_name: String, pub status: HealthStatus, pub rtt_ms: Option<f64>, pub response: Option<HealthResponse>, pub error: Option<String>, }它被嵌套在FanoutResponse(对外即HealthFanoutResponse)中:
pub struct FanoutResponse { pub datacenters: Vec<DatacenterHealth>, }也就是说,一次/health/fanout调用会返回所有已配置数据中心的健康状态数组,而数组中的每一项就是一个DatacenterHealth实例。
二、字段完整说明
下表完整继承了 DatacenterHealth.md 中的属性定义:
| 字段名 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
datacenter_label | i32(Rust SDK 为 i32,服务端内部为 u16) | 数据中心标签,用于唯一标识某个数据中心 | 必填 |
datacenter_name | String | 数据中心名称 | 必填 |
error | Option<String> | 探测失败时的错误信息 | 可选 |
response | Option<HealthResponse> | 成功时的健康响应详情 | 可选 |
rtt_ms | Option<f64> | 探测往返时延,单位毫秒 | 可选 |
status | HealthStatus | 健康状态枚举 | 必填 |
字段间存在明显的组合关系,可以归纳为两种结果形态:
- 成功形态:
status = Ok,同时携带response(健康响应详情)与rtt_ms(往返时延),error为None; - 失败形态:
status = Error,同时携带error(错误描述)与rtt_ms,response为None。
需要特别指出的是,无论成功还是失败,rtt_ms都会被填充(见下文源码分析),它代表这次探测实际消耗的时间,因此不能仅凭rtt_ms是否存在来判断健康状态,必须以status字段为准。
2.1 关联类型:HealthStatus
status字段的类型是 HealthStatus 枚举,序列化值如下:
| 枚举变体 | 序列化值 |
|---|---|
Ok | ok |
Error | error |
对应服务端定义(health.rs)使用了#[serde(rename_all = "snake_case")],因此 JSON 传输中为小写字符串"ok"/"error"。
2.2 关联类型:HealthResponse
成功时的response字段类型为 HealthResponse,包含三个必填字符串字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
runtime | String | 运行时标识 |
status | String | 状态描述 |
version | String | 版本号 |
在服务端源码中,本地数据中心探测会直接构造该响应,runtime固定为"engine"、status固定为"ok"、version取编译时的CARGO_PKG_VERSION(见 health.rs)。
三、SDK 中的序列化细节:Rust / TypeScript / Go
DatacenterHealth模型在多个官方 SDK 中均有对应实现,理解各语言的命名与序列化映射有助于跨语言联调。
3.1 Rust SDK
Rust 版本实现位于 engine/sdks/rust/api-full/rust/src/models/datacenter_health.rs,由 OpenAPI Generator 生成,特点如下:
- 所有字段使用
#[serde(rename = "...")]显式声明 JSON 字段名为snake_case; - 三个可选字段(
error、response、rtt_ms)使用serde_with::rust::double_option包裹,并配合skip_serializing_if = "Option::is_none",可区分"字段缺失"与"显式 null"两种语义; - 提供了便捷构造函数
new(datacenter_label, datacenter_name, status),可选字段默认置为None(见 datacenter_health.rs)。
一个典型的 Rust 使用示例:
use rivet_api_public::models::{DatacenterHealth, HealthStatus}; // 构造 let health = DatacenterHealth::new(1, "us-east".to_string(), HealthStatus::Ok); // 反序列化(JSON 字段为 snake_case) let json = r#"{ "datacenter_label": 1, "datacenter_name": "us-east", "status": "ok", "rtt_ms": 12.5, "response": { "runtime": "engine", "status": "ok", "version": "2.3.14" } }"#; let parsed: DatacenterHealth = serde_json::from_str(json).expect("parse");3.2 TypeScript SDK
TypeScript 版本位于 engine/sdks/typescript/api-full/src/api/types/DatacenterHealth.ts,接口字段采用camelCase命名(datacenterLabel、datacenterName、rttMs),而序列化层 engine/sdks/typescript/api-full/src/serialization/types/DatacenterHealth.ts 通过core.serialization.property("rtt_ms", ...)将 camelCase 字段映射回 wire 格式的 snake_case,保证与 HTTP JSON 报文一致。
3.3 Go SDK
Go 版本位于 engine/sdks/go/api-full/types.go,字段同样以 snake_case JSON tag 暴露,与 Rust / TypeScript 的传输格式保持一致。
所有 SDK 的 schema 定义最终统一来源于 engine/artifacts/openapi.json(DatacenterHealth组件位于其中),这保证了跨语言的一致性。
四、服务端实现:/health/fanout 如何工作
理解字段含义之后,再来看服务端是如何产生这些数据的。核心逻辑位于 engine/packages/api-public/src/health.rs 的fanout/fanout_inner:
- 鉴权:
fanout_inner首先调用ctx.auth().await?,要求调用方具备数据中心读取权限(该接口在 OpenAPI 中声明了bearer_auth安全方案,见 HealthApi.md)。 - 枚举数据中心:从
ctx.config().topology().datacenters读取全部已配置的数据中心,逐个发起探测。 - 本地数据中心直查:如果某个数据中心的
datacenter_label等于当前进程所属数据中心标签(ctx.config().dc_label()),则不走网络请求,直接本地构造HealthResponse,状态固定为Ok。 - 远程数据中心 HTTP 探测:对远程数据中心,通过
send_health_checks并发发起两个 HTTP GET 请求(详见下文)。 - 并发扇出:使用
buffer_unordered(16)将全部数据中心的探测任务并发执行,最多同时进行 16 个,全部完成后汇总为Vec<DatacenterHealth>返回。
4.1 远程探测的细节
send_health_checks(health.rs)对每个远程数据中心同时发起两个健康检查:
{peer_url}/health:对等节点(peer)的健康端点;{proxy_url}/health:代理(proxy)的健康端点。
两个请求通过tokio::try_join!并发执行,各自带5 秒超时(timeout(Duration::from_secs(5)))。判定逻辑为:
peer检查必须成功(非 2xx 即判定失败并bail!);proxy检查成功后,将其 JSON 响应解析为HealthResponse作为最终结果。
失败时,错误信息被记录到tracing::warn!日志,并写入DatacenterHealth.error字段,status置为Error。
4.2 RTT 的度量方式
无论本地还是远程,每个数据中心的探测任务都会在开始时记录Instant::now()(health.rs),完成后用start.elapsed().as_secs_f64() * 1000.0计算毫秒级时延写入rtt_ms。因此rtt_ms反映的是包含网络往返在内的整体探测耗时,可用于评估各数据中心的链路质量。
五、调用方式与返回示例
health_fanout接口定义见 HealthApi.md:
- HTTP 方法:
GET - 路径:
/health/fanout - 参数:无
- 鉴权:Bearer Token(
bearer_auth) - Accept:
application/json - 返回类型:HealthFanoutResponse
一次典型的响应报文如下:
{ "datacenters": [ { "datacenter_label": 0, "datacenter_name": "local-dc", "status": "ok", "rtt_ms": 0.023, "response": { "runtime": "engine", "status": "ok", "version": "2.3.14" } }, { "datacenter_label": 1, "datacenter_name": "remote-dc-a", "status": "error", "rtt_ms": 5120.4, "error": "Proxy health check returned status: 503" } ] }上例中第一个元素对应本地数据中心(时延极低、直接本地构造),第二个元素对应远程数据中心探测失败(status = error,rtt_ms接近 5 秒超时上限,response为null)。这也印证了前文的字段组合规律:成功看response,失败看error,时延始终在rtt_ms。
六、实用要点小结
DatacenterHealth是/health/fanout返回数组中"单个数据中心"的健康快照,必须与 HealthFanoutResponse 配合使用。- 判断健康与否只能看
status(ok/error),不要依赖response或error是否存在来推断。 rtt_ms成功失败都会返回,接近 5 秒通常意味着远程探测超时。- 各 SDK 的 JSON 传输格式统一为
snake_case;TypeScript 接口层使用 camelCase,序列化层负责映射。 - 服务端通过
buffer_unordered(16)并发探测全部数据中心,本地直查、远程走 peer/proxy 双端点(各 5 秒超时),实现细节可继续阅读 engine/packages/api-public/src/health.rs 及 OpenAPI 定义 中的DatacenterHealth组件。
【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考