后端与接口
用 Cloudflare Durable Objects…
在 Cloudflare Workers 上实现需要强一致性、按实体隔离状态或持久连接的有状态协调逻辑时,用 Durable Objects 并遵循其规则与反模式。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 Cloudflare Durable特定
解决思路
- 优先检索而非依赖预训练知识:实现任何 Durable Objects 功能前,先拉取相关文档页面。可用资源包括 Docs(https://developers.cloudflare.com/durable-objects/)、API Reference(https://developers.cloudflare.com/durable-objects/api/)、Best Practices(https://developers.cloudflare.com/durable-objects/best-practices/)、Examples(https://developers.cloudflare.com/durable-objects/examples/)、Roles and permissions(https://developers.cloudflare.com/workers/authorization/durable-objects/)。
- 参考本地参考文档:`./references/rules.md`(核心规则、存储、并发、RPC、alarms)、`./references/testing.md`(当前 Vitest 文档、迁移选择与测试选择)、`./references/workers.md`(Workers handlers、类型、wrangler 配置、可观测性)。
- 需要查 API 时搜索关键词:`blockConcurrencyWhile`、`idFromName`、`getByName`、`setAlarm`、`sql.exec`。
- 在 `wrangler.jsonc` 中配置 durable_objects bindings 与 migrations,例如:`"durable_objects": { "bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }] }`,以及 `"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]`。
- 按基本模式实现 Durable Object:从 `cloudflare:workers` 导入 `DurableObject`,定义 `Env` 接口(`MY_DO: DurableObjectNamespace<MyDurableObject>`),让类继承 `DurableObject<Env>`,构造函数中调用 `super(ctx, env)` 并用 `ctx.blockConcurrencyWhile()` 执行建表 SQL。
- 在 DO 类中通过 RPC 方法暴露操作,例如 `async addItem(data: string): Promise<number>`,内部用 `this.ctx.storage.sql.exec<{ id: number }>("INSERT INTO items (data) VALUES (?) RETURNING id", data)` 并返回 `result.one().id`。
- 在 Worker 的 `fetch` 中通过 `env.MY_DO.getByName("my-instance")` 获取 stub 并调用 RPC 方法,返回 `Response.json({ id })`。
- 遵守关键规则:围绕协调原子建模(每个聊天室/游戏/用户一个 DO,而非一个全局 DO)。
- 用 `getByName()` 做确定性路由:相同输入得到同一个 DO 实例。
- 使用 SQLite 存储:在 migrations 中配置 `new_sqlite_classes`。
- 只在构造函数中用 `blockConcurrencyWhile()` 做 schema 初始化。
- 使用 RPC 方法而不是 fetch() handler(compatibility date >= 2024-04-03)。
- 先持久化再缓存:始终先写入 storage,再更新内存状态。
- 每个 DO 只能有一个 alarm:`setAlarm()` 会替换已有 alarm。
- 授权方面:Durable Objects 没有单独的 roles 或 permissions,访问权限跟随实现它的 Worker;授予可观测性或 Data Studio 访问前先检索最新的 Durable Objects authorization 指南,并将 Workers role 限定到目标 Worker 或 Workers 产品。
- 创建 stub 的三种方式:确定性路由优先用 `env.MY_DO.getByName("room-123")`;从已有 ID 字符串创建用 `env.MY_DO.idFromString(storedIdString)` 再 `env.MY_DO.get(id)`;创建新的唯一 ID 用 `env.MY_DO.newUniqueId()` 再 `env.MY_DO.get(id)`,并在外部存储映射关系。
- 存储操作:SQL 用同步的 `this.ctx.storage.sql.exec(...)`(如 `INSERT INTO t (c) VALUES (?)`)和 `.toArray()` 读取;KV 用异步的 `await this.ctx.storage.put("key", value)` 与 `await this.ctx.storage.get<Type>("key")`。
- Alarm 操作:用 `await this.ctx.storage.setAlarm(Date.now() + 60_000)` 调度(会替换已有 alarm);实现 `async alarm(): Promise<void>` 处理调度工作,可选择再次 `setAlarm` 重新调度;用 `await this.ctx.storage.deleteAlarm()` 取消。
- 测试前先阅读 testing reference(`./references/testing.md`),按其指引完成当前 setup、API、示例与行为覆盖选择,再配置测试套件或编写 Durable Object 测试。
- 避免反模式(NEVER):不要用单个全局 DO 处理所有请求(瓶颈);不要在每个请求上使用 `blockConcurrencyWhile()`(会杀死吞吐);不要把关键状态只存在内存中(驱逐或崩溃会丢失);不要在相关存储写入之间使用 `await`(破坏原子性);不要在 `fetch()` 或外部 I/O 期间持有 `blockConcurrencyWhile()`。
适用边界 · 注意事项
- 无状态请求处理应使用普通 Workers,而不是 Durable Objects。
- 需要最大化全球分布的场景不适合 Durable Objects。
- 高扇出的独立请求不适合用 Durable Objects。