mcpField
C
chat-room

chat-room

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

关于 chat-room

面向使用 RivetKit 构建实时聊天室后端的开发者技能。示例采用每个房间一个 Actor,以 SQLite 保存消息历史,并通过事件向已连接客户端广播新消息。

主要功能

  1. 01每个房间使用独立 Actor
  2. 02SQLite 保存消息历史
  3. 03按消息 ID 顺序读取历史
  4. 04广播新消息事件
  5. 05React 前端示例

适用场景

  1. 01构建按房间隔离的聊天室
  2. 02持久化并加载房间消息记录
  3. 03向所有在线房间客户端广播消息

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rivet-dev/skills --skill chat-room

安装后在 Claude Code 中输入/chat-room确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

BASE_SKILL.md
index.json
openapi.json
reference/actors/
access-control.md
actions.md
actor-runtime-socket.md
appearance.md
authentication.md
communicating-between-actors.md
connections.md
debugging.md
design-patterns.md
destroy.md
errors.md
events.md
fetch-and-websocket-handler.md
helper-types.md
http-api.md
input.md
inspector-tabs.md
keys.md
kv.md
lifecycle.md
limits.md
metadata.md
queues.md
reference/actors/quickstart/
backend.md
cloudflare.md
effect.md
next-js.md

详细介绍

聊天室

重要:开始任何操作之前,必须先阅读此技能目录中的 BASE_SKILL.md。其中包含有关调试、错误处理、状态管理、部署和项目设置的重要指导。这些规则和模式适用于所有 RivetKit 工作。以下内容均假设你已阅读并理解这些内容。

可运行示例

如需参考实现,请阅读这些模板中的原始可运行示例代码:

使用 RivetKit 构建聊天室后端的模式:房间级 Actor、持久化消息历史记录,以及通过 WebSocket 连接实时传递消息。

入门代码

从 GitHub 上的可运行示例开始并进行改造。后端是一个 chatRoom Actor;前端是使用 @rivetkit/react 的 React 应用(参见 React 快速入门)。

主题摘要
房间模型每个房间键对应一个 chatRoom Actor。前端默认将键设为 general;输入不同的房间名称会连接到不同的 Actor。
历史记录在 db({ onMigrate }) 中创建 SQLite messages 表,并使用 ORDER BY id ASC 读取。
消息传递sendMessage 插入数据行,然后向每个已连接的客户端广播类型化的 newMessage 事件。
身份示例中未设置身份。sender 是普通的操作参数;用于生产环境时,应将身份绑定到连接。

每个房间对应一个 Actor 的模型

每个房间都是一个 Rivet Actor 实例,通过 键寻址。客户端调用 useActor({ name: "chatRoom", key: [roomId] }),获取或创建该房间对应的 Actor。这样可以实现:

  • 隔离:每个房间的历史记录和连接都完全限定在其键范围内。切换房间输入会为 hook 重新设置键,并连接到拥有独立历史记录的另一个 Actor。
  • 单一串行写入者:同一房间的所有 sendMessage 调用都由一个 Actor 处理,因此无需锁即可保证消息顺序一致。SQLite AUTOINCREMENT id 是规范顺序,因此 getHistory 按 id 而不是时间戳排序。
  • 自然扩展:各个房间可在集群中独立分布。繁忙的房间不会拖慢其他房间。

消息历史记录存储

此示例将历史记录存储在 Actor 的 SQLite 数据库中,而不是 JSON 状态中。请根据历史记录的大小和查询需求进行选择:

方案适用场景实现指导
SQLite(此示例采用的方案)需要排序、上限、分页或搜索的大量或长期历史记录在 db({ onMigrate }) 中创建 messages 表,使用参数化查询插入数据(c.db.execute("INSERT ... VALUES (?, ?, ?)", ...)),并通过 ORDER BY id ASC 读取。历史记录在 Actor 休眠后仍会保留,且可扩展到超出内存存储能力的规模。
JSON 状态少量近期历史记录,例如最近 50 到 100 条消息将消息添加到 Actor 状态中的 messages 数组,并在每次发送时截断至上限。这是最简单的方案,但全部历史记录都存放在内存中,且没有查询层,因此仅适用于近期历史记录数量有限的场景。

广播消息投递

新消息通过类型化的 事件 发送给已连接的客户端:

  • Actor 声明 events: { newMessage: event() },其中 Message 为 { sender, text, timestamp }。
  • sendMessage 操作 使用服务端的 Date.now() 时间戳构建消息,将其插入 messages 表,然后调用 c.broadcast("newMessage", message) 并将消息返回给调用方。
  • 每个客户端通过 useEvent("newMessage", ...) 订阅,并将新消息追加到本地列表。发送者也通过与其他人相同的广播路径呈现自己的消息,因此所有客户端都使用同一条代码路径。
  • 历史记录加载受连接状态控制:连接就绪后,客户端调用一次 getHistory() 来呈现历史记录,之后的所有内容都依赖事件接收。

对于面向整个房间的消息,请使用 c.broadcast(...)。对于私密或面向特定接收者的有效载荷(例如房间内的私信),请改为发送到单个连接;这是对此示例的推荐扩展。

正在输入指示器和在线状态(扩展)

此示例未实现任何类型的正在输入指示器、在线状态或加入/离开处理。代码中没有 createConnState、onConnect 或 onDisconnect。如有需要,请将其作为临时的 连接 行为添加:

  • 保持临时性:将用户名和正在输入标志存储在每个连接的状态中,绝不写入 SQLite 或持久化的 Actor 状态。在线状态根据活动连接得出,连接断开时应随之消失。
  • 仅在状态变化时广播:用户开始或停止输入时发送输入事件,并在 onConnect / onDisconnect 中发送在线状态事件,而不是轮询或定时触发。
  • 在客户端设置过期时间:在客户端短暂超时后清除正在输入指示器,避免连接意外断开后留下卡住的“正在输入”提示。

每用户收件箱(扩展)

如需离线消息投递、私信、未读计数或通知扇出,请为每个用户添加一个 userInbox[userId] Actor。此扩展不属于示例内容:

  • 房间 Actor 通过 Actor 间调用 将每条消息转发到每位成员的收件箱 Actor,因此即使用户未连接到房间,也能累积消息。
  • 收件箱 Actor 管理每位用户的未读状态,并在用户上线时提供这些状态,不受用户所在房间的影响。
  • 私信可以视为一种特殊的房间:可以使用以用户 ID 排序后的用户 ID 对作为键的 chatRoom,也可以直接从一个收件箱投递到另一个收件箱(如果不需要共享历史记录语义)。

Actors

  • 键:chatRoom[roomId]
  • 职责:管理一个聊天室。将聊天室的消息历史记录保存在其 SQLite 数据库中,并向每个已连接的客户端广播每条新消息。
  • 操作
    • sendMessage
    • getHistory
  • 队列
    • 无
  • 事件
    • newMessage
  • 状态
    • SQLite
    • messages 表:id(自增主键)、sender、text、timestamp

生命周期

sequenceDiagram
	participant A as Client A
	participant B as Client B
	participant R as chatRoom

	A->>R: connect with key [roomId]
	Note over R: every start runs onMigrate (CREATE TABLE IF NOT EXISTS messages)
	A->>R: getHistory()
	R-->>A: Message[] ordered by id
	B->>R: connect with key [roomId]
	B->>R: getHistory()
	R-->>B: Message[] ordered by id
	A->>R: sendMessage(sender, text)
	Note over R: INSERT row with server timestamp
	R-->>A: newMessage (broadcast)
	R-->>B: newMessage (broadcast)
	A->>R: disconnect
	Note over R: history stays in SQLite for the next connection

安全检查清单

此示例有意保持精简,并跳过了以下所有内容。投入生产前,请添加这些内容:

  • 加入前进行身份验证:任何客户端只要知道房间名称,就可以加入任意房间;而且每次调用中的 sender 都是任意客户端输入。请在连接身份验证期间验证令牌,将身份绑定到连接状态,并在提供历史记录前检查房间成员资格。切勿信任作为操作参数传入的发送者名称。
  • 限制消息长度:此示例接受空消息,且没有长度限制。请在服务端进行修剪、拒绝空文本,并限制最大长度。
  • 按连接进行速率限制:对每个连接的 sendMessage 进行速率限制,以阻止垃圾消息和广播放大。
  • 服务端时间戳和 ID:此示例已正确处理。timestamp 在操作内部通过 Date.now() 获取,id 则来自 SQLite AUTOINCREMENT。请保持这种方式;切勿接受客户端提供的时间戳或 ID。
  • 限制历史记录数量:getHistory 会返回所有行,没有任何限制。请添加 LIMIT 和分页,并清理或归档旧行,以免长期运行的房间无限增长。
  • 参数化查询:此示例已使用 ? 占位符进行插入。请确保所有用户提供的文本都不会进入 SQL 字符串插值。

参考索引

Actors
Cli
客户端
实用指南
部署
通用
自托管

评论

支持 Markdown