mcpField
R
rivetkit-client-rust

rivetkit-client-rust

github.com/rivet-dev开发44888 次安装1 天前更新
代码仓库

关于 rivetkit-client-rust

用于通过 `rivetkit::client` 构建 Rust 客户端并连接 Rivet Actors 的技能,介绍类型化操作与事件、连接处理、配置及后端调用。

主要功能

  1. 01类型化 Actor 操作与事件
  2. 02无状态调用和持久连接
  3. 03类型化事件订阅
  4. 04带退避机制的自动重连
  5. 05底层 HTTP 与 WebSocket 调用

适用场景

  1. 01在 Rust 应用中调用 Rivet Actors
  2. 02通过连接接收类型化 Actor 事件
  3. 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

入门步骤

  1. 添加依赖
    cargo add rivetkit anyhow async-trait
    cargo add serde --features derive
    cargo add tokio --features full
    
  2. 使用 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 技能获取后端指南。

评论

支持 Markdown