关于 rivetkit-client-rust
用于通过 `rivetkit::client` 构建 Rust 客户端并连接 Rivet Actors 的技能,介绍类型化操作与事件、连接处理、配置及后端调用。
主要功能
- 01类型化 Actor 操作与事件
- 02无状态调用和持久连接
- 03类型化事件订阅
- 04带退避机制的自动重连
- 05底层 HTTP 与 WebSocket 调用
适用场景
- 01在 Rust 应用中调用 Rivet Actors
- 02通过连接接收类型化 Actor 事件
- 03从 Tokio 后端处理程序调用 Actors
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rivet-dev/ skills --skill rivetkit-client-rust安装后在 Claude Code 中输入/rivetkit-client-rust确认已加载。
附带文件index.json
详细介绍
RivetKit Rust 客户端
构建使用 rivetkit::client 连接 Rivet Actors 的 Rust 客户端时,请使用此技能。
版本
RivetKit 版本:2.3.7
入门步骤
- 添加依赖
cargo add rivetkit anyhow async-trait cargo add serde --features derive cargo add tokio --features full - 使用
Client::new(ClientConfig::new(endpoint))创建客户端,并通过get_or_create_typed_default::<A>(...)调用类型化操作。
错误处理策略
- 默认优先采用快速失败行为。使用
?传播anyhow::Result。 - 除非确有必要,否则避免用宽泛的
match分支吞掉错误。 - 如果在内联代码中处理错误,请明确处理,至少记录日志。
Rust 支持仍处于 Beta 阶段。受支持的公开 Rust API 为 rivetkit 和 rivetkit::client;更底层的 crate 属于内部实现细节,不提供稳定性保证。请参阅 docs.rs/rivetkit 上的完整 API 参考,或可运行的 hello-world-rust 示例。
开始使用
请参阅 Rust 快速入门指南以开始使用。
安装
添加 rivetkit crate 及其配套依赖:
cargo add rivetkit anyhow async-trait
cargo add serde --features derive
cargo add tokio --features full
Rust 客户端采用强类型设计。它与 Actor 共用相同的操作和事件类型,因此请在 src/lib.rs 中定义 Actor,并在服务端和客户端都导入这些类型。无需在客户端重新定义 Actor。请参阅快速入门中的定义 Actor,了解本页所基于的 Actor 定义。
最简客户端
use counter::{Counter, Increment};
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let counter = client.get_or_create_typed_default::<Counter>("counter", ["my-counter"])?;
let count = counter.send(Increment { amount: 1 }).await?;
println!("New count: {count}");
Ok(())
}
这里的 counter 是你的 crate 名称(即 Cargo.toml 中的包 name,其中连字符需改为下划线)。Counter 和 Increment 是你与 Actor 一起定义的类型。
无状态与有状态
use counter::{Counter, Increment, NewCount};
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let counter = client.get_or_create_typed_default::<Counter>("counter", ["my-counter"])?;
// Stateless: each call is independent
counter.send(Increment { amount: 1 }).await?;
// Stateful: keep a connection open for realtime events
let connection = counter.connect();
connection
.on::<NewCount>(|event| println!("count: {}", event.count))
.await;
connection.send(Increment { amount: 1 }).await?;
connection.disconnect().await;
Ok(())
}
在句柄上进行无状态调用时,每个操作都会发起一个短暂请求。连接会保持 WebSocket 打开,以便接收事件,并在多次调用间复用。
获取 Actors
use counter::Counter;
use rivetkit::{
client::{Client, ClientConfig, GetOrCreateOptions},
prelude::*,
TypedClientExt,
};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
// Get or create an actor
let room = client.get_or_create_typed_default::<Counter>("counter", ["room-42"])?;
// Get an existing actor handle (fails when used if the actor does not exist)
let existing = client.get_typed_default::<Counter>("counter", ["room-42"])?;
// Create a new actor with input
let created = client.get_or_create_typed::<Counter>(
"counter",
["game-1"],
GetOrCreateOptions {
create_with_input: Some(json!({ "mode": "ranked" })),
..Default::default()
},
)?;
// Get an actor handle by ID
let by_id = client.get_for_id("counter", "actor-id", Default::default())?;
// Resolve the actor ID
let resolved_id = room.inner().resolve().await?;
println!("Resolved ID: {resolved_id}");
Ok(())
}
get_typed_default / get_or_create_typed_default 使用默认选项。非默认变体(get_typed / get_or_create_typed)接收 GetOptions / GetOrCreateOptions,用于设置连接参数、输入和区域。在 GetOrCreateOptions 或 CreateOptions 上设置 pool_name,即可为单个 Actor 覆盖客户端配置的池。
连接参数
通过句柄选项传递连接参数。这些参数会传递给 Actor 的 create_conn_state 回调:
use counter::Counter;
use rivetkit::{
client::{Client, ClientConfig, GetOrCreateOptions},
prelude::*,
TypedClientExt,
};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let chat = client.get_or_create_typed::<Counter>(
"counter",
["general"],
GetOrCreateOptions {
params: Some(json!({ "authToken": "jwt-token-here" })),
..Default::default()
},
)?;
let connection = chat.connect();
connection.disconnect().await;
Ok(())
}
订阅事件
on 为某个事件注册类型化回调,并在订阅注册完成后返回:
use counter::{Counter, NewCount};
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let connection = client
.get_or_create_typed_default::<Counter>("counter", ["general"])?
.connect();
connection
.on::<NewCount>(|event| println!("count changed: {}", event.count))
.await;
Ok(())
}
事件回调是同步的,会针对每个匹配的事件运行。Actor 发出的事件类型(此处为 NewCount)会为你解码为类型化值。
连接生命周期
较低层的连接会公开生命周期回调和当前状态。通过 connection.inner() 获取该连接:
use counter::Counter;
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let connection = client
.get_or_create_typed_default::<Counter>("counter", ["general"])?
.connect();
let inner = connection.inner().clone();
inner.on_open(|| println!("connected")).await;
inner.on_close(|| println!("disconnected")).await;
inner.on_error(|message| eprintln!("error: {message}")).await;
inner
.on_status_change(|status| println!("status: {status:?}"))
.await;
println!("current status: {:?}", inner.conn_status());
connection.disconnect().await;
Ok(())
}
ConnectionStatus 的值为 Idle、Connecting、Connected 或 Disconnected 之一。连接会自动通过退避机制重连,直到你调用 disconnect。
底层 HTTP 与 WebSocket
对于实现了 on_request 或 on_websocket 的 Actor,请直接在非类型化句柄(handle.inner())上调用它们。fetch 返回 reqwest::Response,而 web_socket 返回 tokio_tungstenite 流。此示例还需要几个额外的 crate:
cargo add futures-util tokio-tungstenite
cargo add reqwest --features json
use counter::Counter;
use futures_util::{SinkExt, StreamExt};
use reqwest::{header::HeaderMap, Method};
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
use tokio_tungstenite::tungstenite::Message;
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let handle = client.get_or_create_typed_default::<Counter>("counter", ["general"])?;
// Raw HTTP request
let response = handle
.inner()
.fetch("history", Method::GET, HeaderMap::new(), None)
.await?;
let history: Vec<String> = response.json().await?;
println!("history: {history:?}");
// Raw WebSocket connection
let mut ws = handle.inner().web_socket("stream", None).await?;
ws.send(Message::text("hello")).await?;
if let Some(message) = ws.next().await {
println!("received: {:?}", message?);
}
Ok(())
}
从后端调用
客户端是常规的 Tokio 类型,因此你可以在后端(Axum、Actix 等)中持有它,并从请求处理程序调用 Actors。客户端实现了 Clone,共享开销很低:
use counter::{Counter, Increment};
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
async fn increment(client: Client) -> Result<i64> {
let counter = client.get_or_create_typed_default::<Counter>("counter", ["server-counter"])?;
let count = counter.send(Increment { amount: 1 }).await?;
Ok(count)
}
错误处理
操作和连接调用会返回 anyhow::Result。Actor 端的错误会以 anyhow::Error 的形式传出,其中包含错误组、代码、消息和元数据:
use counter::{Counter, Increment};
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let counter = client.get_or_create_typed_default::<Counter>("counter", ["my-counter"])?;
match counter.send(Increment { amount: 1 }).await {
Ok(count) => println!("count: {count}"),
Err(error) => eprintln!("action failed: {error:#}"),
}
Ok(())
}
概念
键
键可唯一标识 Actor 实例。使用复合键(数组)进行分层寻址:
use counter::Counter;
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
// Compound key: [org, room]
let room = client.get_or_create_typed_default::<Counter>("counter", ["org-acme", "general"])?;
let actor_id = room.inner().resolve().await?;
println!("Actor ID: {actor_id}");
Ok(())
}
键接受 &str 或 String 数组(["org-acme", "general"])。当 user_id 包含用户数据时,不要使用字符串插值(例如 format!("org:{user_id}"))构建键。请改用数组,以防止键注入攻击。
配置
ClientConfig::new(endpoint) 是一个构建器。端点始终为必填项;没有默认值。常用选项:
use rivetkit::client::ClientConfig;
let config = ClientConfig::new("http://localhost:6420")
.namespace("default")
.token("pk_...")
.pool_name("my-pool")
.header("x-custom", "value");
namespace- 目标命名空间(默认为引擎配置的命名空间)。token- 引擎的身份验证令牌。pool_name- 要使用的运行器池。header/headers- 随每个请求发送的额外 HTTP 标头。max_input_size- 限制编码后的操作输入大小。
对于无服务器部署,请将端点设置为应用的 /api/rivet URL。详情请参阅端点。
API 参考
请参阅 docs.rs/rivetkit-client 上的完整客户端 API 文档。
需要的不止客户端?
如果你还需要了解 Rivet Actors、注册表或服务端 RivetKit,请添加主技能:
npx skills add rivet-dev/skills
然后使用 rivetkit 技能获取后端指南。