Rivet Actors GetOrCreate API 深度解析:跨数据中心幂等 Actor 获取与创建
2026/9/18 4:05:40 网站建设 项目流程

Rivet Actors GetOrCreate API 深度解析:跨数据中心幂等 Actor 获取与创建

【免费下载链接】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 Actors 中,actors_get_or_create(HTTPPUT /actors)是构建有状态工作负载时的核心原语:它以namespace + name + key为唯一标识,实现"存在则返回、不存在则创建"的幂等语义,特别适合 AI Agent、协作类应用与持久化执行等场景。本文以 ActorsGetOrCreateApi.md 为主体,结合仓库中 api-public / api-peer / pegboard 的源码实现,完整讲解该接口的请求参数、返回结构、跨数据中心路由策略(Datacenter Round Trips)、Epoxy Key 预留机制与典型错误处理,帮助你掌握在 Rust SDK 中正确、高效地使用这一原语。


一、接口概览:GetOrCreate 与 Create 的定位差异

actors_get_or_create由 api-public 路由 以PUT /actors注册,与POST /actors(ActorsCreateApi)相比,核心差异在于:

维度POST /actors(actors_create)PUT /actors(actors_get_or_create)
语义无条件创建,key 冲突会触发duplicate_key错误幂等:key 已存在则直接返回既有 Actor,否则创建
响应ActorsCreateResponse,仅含actorActorsGetOrCreateResponse,含actorcreated标志
适用场景需要严格"新建"的语义,或客户端已确认 Actor 不存在每次请求都"拿引用",如连接游戏房间、绑定会话、按业务键定位实例

从 SDK 生成代码看,客户端方法签名如下(见 actors_get_or_create_api.rs):

pub async fn actors_get_or_create( configuration: &configuration::Configuration, namespace: &str, actors_get_or_create_request: models::ActorsGetOrCreateRequest, ) -> Result<models::ActorsGetOrCreateResponse, Error<ActorsGetOrCreateError>>

请求的 base URL 相对http://localhost,需要bearer_auth鉴权,Content-TypeAccept均为application/json


二、请求参数与数据模型

PUT /actors的参数分两部分:query 参数namespace与 JSON bodyActorsGetOrCreateRequest。对应的 Rust 模型定义在 actors_get_or_create_request.rs。

2.1 参数总表

名称位置类型必填说明
namespaceQueryString目标命名空间名称
nameBodyStringActor 的名称(与key共同构成唯一性约束)
keyBodyString业务键,最大 1024 字节,不可为空
runner_name_selectorBodyString选择器,决定由哪个 Runner 配置池承载该 Actor
crash_policyBodyCrashPolicy崩溃策略:restart/sleep/destroy
datacenterBodyOption<String>目标数据中心名称;不传时自动选择
inputBodyOption<String>创建时注入的初始数据,为任意 base64 编码的二进制数据

2.2 关键字段细节

  • key 的约束:服务端在 api-peer 的 get_or_create 中校验:空 key 返回EmptyKey;超过 1024 字节返回KeyTooLarge(错误定义见 errors.rs)。MAX_ACTOR_KEY_SIZE = 1024在源码中以常量形式声明。
  • crash_policy 枚举:见 CrashPolicy,取值restartsleepdestroy,分别表示崩溃后重启、进入休眠、直接销毁。
  • input:与ActorsCreateRequest一致(见 ActorsCreateRequest),是可选的 base64 二进制数据,会在 Actor 启动时注入。

三、响应结构:actor + created

响应模型ActorsGetOrCreateResponse(见 actors_get_or_create_response.rs)包含两个字段:

字段类型说明
actorActor完整的 Actor 对象
createdbooltrue表示本次调用实际创建了新 Actor;false表示命中已存在的 Actor

created字段是幂等语义的关键:客户端可以借此区分"首次启动"(需要初始化状态、推送初始数据)与"复用已有实例"(直接连接)。

Actor对象本身携带丰富的生命周期信息(见 Actor.md):

  • actor_idnamespace_idnamekeydatacenterrunner_name_selector
  • create_ts:首次创建时间;start_ts:首次可连接时间(null表示从未)
  • sleep_ts:进入休眠状态的时间;reschedule_ts:下次尝试调度的最早时间戳
  • connectable_ts:最后一次可连接时间(未运行时为null
  • destroy_tspending_allocation_tserror:销毁时间、等待分配时间与启动失败的错误详情

四、核心原理:Datacenter Round Trips 与跨数据中心路由

原文档最重要的部分是Datacenter Round Trips分析,它揭示了actors_get_or_create在不同情形下的跨数据中心网络开销。综合服务端实现 get_or_create.rs,可以还原完整流程。

4.1 三种情形的往返次数

情形 A:Actor 已存在(2 次往返)

  1. namespace::ops::resolve_for_name_global:将 namespace 名称解析为全局唯一的namespace_id
  2. GET /actors/{id}:直接定位并读取既有 Actor。

情形 B:Actor 不存在,且在"当前"数据中心创建(2 次往返)

  1. namespace::ops::resolve_for_name_global
  2. pegboard::workflows::actor创建流程(包含 Epoxy Key 分配),即 Actor 创建工作流。

情形 C:Actor 不存在,且在"其他"数据中心创建(3 次往返)

  1. namespace::ops::resolve_for_name_global
  2. POST /actors转发到远端数据中心;
  3. 远端执行pegboard::workflows::actor创建流程(包含 Epoxy Key 分配)。

原文档同时强调:actor::get 永远发生在同一数据中心内——也就是说,一旦 Actor 被创建,后续对该 Actor 的所有操作(读取、连接、KV 访问)都在其所在的数据中心完成,不会产生跨 DC 的读取。

4.2 数据中心选择的实现细节

请求处理在get_or_create_inner中先解析 namespace,然后调用find_dc_for_actor_creation决定目标数据中心(见 utils.rs):

  • 若请求带datacenter参数,则通过dc_for_name解析为 datacenter label,并校验该 DC 是否启用了对应的 Runner 配置;
  • 若未指定,则取list_runner_config_enabled_dcs返回的第一个可用 DC;
  • 若没有任何 DC 配置了该 runner 名称,返回NoRunnerConfigConfigured错误。

选定目标 DC 后,get_or_create_inner做本地 / 转发决策(见 get_or_create.rs):

if target_dc_label == ctx.config().dc_label() { rivet_api_peer::actors::get_or_create::get_or_create(ctx.into(), (), query, body).await } else { request_remote_datacenter::<GetOrCreateResponse>( ctx.config(), target_dc_label, "/actors", axum::http::Method::PUT, Some(&query), Some(&body), ).await }

即:目标 DC 就是当前 DC 时走进程内操作(api-peer),否则通过 HTTP 转发到远端 DC 的/actors端点——这正对应情形 C 中的第 2 次往返。


五、源码级实现剖析:从 Key 查找到并发兜底

api-peer中的核心逻辑(见 get_or_create.rs)由三步组成:查 key → 命中即返回 → 未命中创建

5.1 第一步:按 Key 查询既有 Actor

通过pegboard::ops::actor::get_for_key操作完成(见 get_for_key.rs),其内部先调用get_reservation_for_keyEpoxy读取该 key 的预留(reservation)信息,输出三种结果:

输出含义处理
Found { actor }key 已有 Actor 且在当前 DC直接返回created: false
NotFound无预留记录进入创建流程
Forward { dc_label }key 被预留到其他 DC返回KeyReservedInDifferentDatacenter错误

5.2 第二步:创建并处理并发竞争

未命中时,代码生成新的actor_idId::new_v1(ctx.config().dc_label()),即 actor ID 内嵌 DC 标签),调用pegboard::ops::actor::create创建,并携带forward_request: true

这里有一个值得注意的并发兜底:如果两个客户端同时以同一 key 调用 get_or_create,创建阶段可能抛出duplicate_key错误。api-peer 通过extract_duplicate_key_error(见 get_or_create.rs)从错误链中提取existing_actor_id,然后get这个已存在的 Actor,同样返回created: false。这样即便在竞争条件下,get_or_create 也保持幂等而非报错。该辅助函数同时支持本地RivetError与远端RawErrorResponse两种错误形态。

5.3 第三步:Epoxy Key 预留的全局语义

创建流程涉及 Epoxy 的per-key Paxos机制(详见 ACTOR_KEY_RESERVATION.md):

  • Epoxy KV 中存储Actor key -> reservation ID,reservation ID 内含 Actor 所在 DC 标签;
  • 全新 key 走 per-key Paxos 快速路径,1 个 RTT 完成预留;
  • 解析使用kv_get_optimistic,假设值一经写入不再变化,因此本地可缓存 Actor 所在 DC,解析一次后无需再跨节点读取;
  • 由于预留值不可变更,key 与数据中心绑定:Actor 只能在原预留 DC 创建,这正是ForwardKeyReservedInDifferentDatacenter错误存在的根本原因(错误定义见 errors.rs)。

另一方面,pegboard::ops::actor::create还会订阅 Actor 工作流的CreateComplete/Failed/DestroyStarted事件并阻塞等待结果(见 create.rs),若工作流报出KeyReservedInDifferentDatacenter且允许转发,则会forward_to_datacenter把请求转到正确 DC 重试——这解释了为何默认不指定datacenter时系统能自动将 Actor 创建到正确的位置。


六、错误码速查

get_or_create 可能触发的典型错误(定义于 pegboard/src/errors.rs):

错误 code触发条件
empty_keykey为空字符串
key_too_largekey超过 1024 字节
duplicate_keykey 冲突(并发下会被 api-peer 自动兜底)
key_reserved_in_different_datacenterkey 已预留到其他 DC,且请求限制了 DC 或无法转发
no_runner_config_configured任何 DC 都没有配置匹配runner_name_selector的 Runner
creation_rate_limit同一 namespace 内创建速率超限
destroyed_during_creation创建过程中 Actor 被销毁
namespace_not_foundnamespace 名称无法解析

七、实战示例:Rust SDK 中的幂等获取

结合前文,一个完整的调用示例如下(类型与函数来自 api-full Rust 客户端):

use rivet_api_full::apis::{configuration::Configuration, actors_get_or_create_api}; use rivet_api_full::models::{ActorsGetOrCreateRequest, CrashPolicy}; let config = Configuration { base_path: "http://localhost".to_string(), bearer_access_token: Some("YOUR_TOKEN".to_string()), ..Default::default() }; let request = ActorsGetOrCreateRequest::new( CrashPolicy::Restart, // 崩溃后重启 "session-42".to_string(), // key:业务唯一键 "chat-room".to_string(), // name:Actor 名称 "default".to_string(), // runner_name_selector:Runner 配置池 ); // datacenter 与 input 为可选字段,可后续设置 // request.datacenter = Some(Some("dc-1".to_string())); // request.input = Some(Some(base64::encode(initial_payload))); let resp = actors_get_or_create_api::actors_get_or_create( &config, "my-namespace", request, ).await?; if resp.created { // 首次创建:初始化状态、推送初始数据 println!("created actor {}", resp.actor.actor_id); } else { // 复用已有 Actor:直接连接 println!("reusing actor {}", resp.actor.actor_id); }

要点回顾:

  • 不传datacenter时,系统依据runner_name_selector自动选择启用了对应 Runner 配置的 DC;
  • 相同的namespace + name + key重复调用只会返回同一个 Actor;
  • 若确实需要跨 DC 创建(如按用户地域就近放置),显式传datacenter可控制放置位置,但要留意 key 一旦预留即与 DC 绑定(见 ACTOR_KEY_RESERVATION.md)。

八、小结

actors_get_or_create是 Rivet Actors 中最实用的"拿引用"原语:它把"查 + 建"两步合并为一次幂等 PUT 请求,通过 Epoxy 的全局 key 预留保证跨数据中心的一致性,并用created标志让调用方精确区分首次创建与复用命中。理解其 Datacenter Round Trips 与底层get_for_key/create/ 转发链路的实现,有助于你在设计多数据中心 Actor 架构时做出更合理的放置与容错决策。

如需深入,可继续阅读 ActorsCreateApi.md(对比无条件创建)、Actor.md(Actor 对象全字段)以及 ACTOR_LIFECYCLE.md(Actor 从创建、运行、休眠到销毁的完整时序)。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询