CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Cloudflare 实战入门/课时/Durable Objects
RawBlame
课时目录12 节

Cloudflare 实战入门

  1. 01边缘计算概览
  2. 02Wrangler 与本地开发
  3. 03Workers 核心
  4. 04Workers KV
  5. 05R2 对象存储
  6. 06D1 数据库
  7. 07Queues 消息队列
  8. 08Durable Objects
  9. 09Workers AI
  10. 10Stream 与媒体
  11. 11安全与防护
  12. 12运维与集成

Durable Objects

强一致状态与协调。

30 分钟

Durable Objects

这一课你将能做到

  1. 创建一个 Durable Object 类并通过 Worker 路由访问
  2. 用 Durable Object 实现强一致的计数器、聊天室、协同编辑状态
  3. 理解 Durable Object 的「单线程」语义和存储模型

为什么要学

KV 最终一致、Queue 至少一次、D1 写走中心——这些都不适合「强一致、低延迟、高并发」的场景。比如:

  • 实时聊天室:谁在线、消息顺序、未读数
  • 协同编辑:多个用户同时改同一文档
  • 游戏房间:玩家状态、回合制、实时同步

人话:Durable Object 就是一个「全局唯一的、有状态的、单线程的」对象实例。全世界只有一个 chatroom-123 实例,所有对 chatroom-123 的请求都会路由到同一个节点、同一个线程、同一份内存状态。这就是强一致的来源。

先建立图像

普通 Worker:                     Durable Object:
                                 
[请求1] ──> [北京节点]            [请求1] ──> [chatroom-123 实例]
[请求2] ──> [纽约节点]            [请求2] ──> [chatroom-123 实例]
[请求3] ──> [伦敦节点]            [请求3] ──> [chatroom-123 实例]
   │            │                  │
   ▼            ▼                  ▼
 无共享状态    无共享状态          共享内存状态 + 持久化存储

跟着做

步骤 1:定义 Durable Object 类

export class ChatRoom {
  state: DurableObjectState;
  
  constructor(state: DurableObjectState) {
    this.state = state;
  }
  
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    
    if (url.pathname === '/join') {
      const userId = url.searchParams.get('userId')!;
      const users = await this.state.storage.get<string[]>('users') ?? [];
      if (!users.includes(userId)) {
        users.push(userId);
        await this.state.storage.put('users', users);
      }
      return Response.json({ users });
    }
    
    if (url.pathname === '/message') {
      const { userId, text } = await request.json();
      const messages = await this.state.storage.get<any[]>('messages') ?? [];
      messages.push({ userId, text, timestamp: Date.now() });
      await this.state.storage.put('messages', messages);
      return Response.json({ messages });
    }
    
    return new Response('Not Found', { status: 404 });
  }
}

步骤 2:在 Worker 中路由到 Durable Object

wrangler.toml:

name = "chat-worker"
main = "src/index.ts"

[[durable_objects.bindings]]
name = "CHAT_ROOM"
class_name = "ChatRoom"
export interface Env {
  CHAT_ROOM: DurableObjectNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const roomId = url.searchParams.get('roomId') ?? 'default';
    
    // 根据 roomId 获取或创建 Durable Object 实例
    const id = env.CHAT_ROOM.idFromName(roomId);
    const stub = env.CHAT_ROOM.get(id);
    
    // 把请求转发给 Durable Object
    return stub.fetch(request);
  },
} satisfies ExportedHandler<Env>;

步骤 3:用 WebSocket 实现实时聊天

export class ChatRoom {
  state: DurableObjectState;
  sessions: Map<WebSocket, string> = new Map();
  
  constructor(state: DurableObjectState) {
    this.state = state;
  }
  
  async fetch(request: Request): Promise<Response> {
    const upgradeHeader = request.headers.get('Upgrade');
    if (upgradeHeader !== 'websocket') {
      return new Response('Expected WebSocket', { status: 426 });
    }
    
    const [client, server] = Object.values(new WebSocketPair());
    await this.handleSession(server);
    
    return new Response(null, { status: 101, webSocket: client });
  }
  
  async handleSession(ws: WebSocket) {
    ws.accept();
    this.sessions.set(ws, 'anonymous');
    
    ws.addEventListener('message', async (event) => {
      const data = JSON.parse(event.data as string);
      
      if (data.type === 'join') {
        this.sessions.set(ws, data.userId);
        this.broadcast({ type: 'join', userId: data.userId });
      }
      
      if (data.type === 'message') {
        this.broadcast({ type: 'message', userId: this.sessions.get(ws), text: data.text });
      }
    });
    
    ws.addEventListener('close', () => {
      const userId = this.sessions.get(ws);
      this.sessions.delete(ws);
      this.broadcast({ type: 'leave', userId });
    });
  }
  
  broadcast(message: any) {
    const payload = JSON.stringify(message);
    for (const [ws] of this.sessions) {
      ws.send(payload);
    }
  }
}

步骤 4:持久化存储

Durable Object 有内置的 storage API,数据会持久化到磁盘:

// 写入
await this.state.storage.put('key', value);

// 读取
const value = await this.state.storage.get('key');

// 事务
await this.state.storage.transaction(async (txn) => {
  const count = await txn.get<number>('count') ?? 0;
  await txn.put('count', count + 1);
});

讲清楚要点

  1. 单线程语义:每个 Durable Object 实例在同一时间只处理一个请求。这意味着你不需要锁,状态天然线程安全。但也意味着慢操作会阻塞后续请求。
  2. 全局唯一:idFromName("room-123") 在全球任何地方都会得到同一个实例 ID,请求会被路由到该实例所在的节点。
  3. 内存 + 持久化:Durable Object 有内存(this.sessions)和持久化存储(this.state.storage)。内存快但重启丢失,持久化慢但可靠。
  4. WebSocket 原生支持:Durable Object 是 Workers 中唯一支持 WebSocket 长连接的方式。配合 hibernation 可以实现低成本长连接。
  5. 计费模式特殊:按「活跃时长」计费。一个 DO 实例只要还在处理请求或持有 WebSocket 连接,就在计费。空闲时会自动休眠。

你可能会懵的地方

  • Durable Object 和普通 Worker 怎么选? 需要强一致、共享状态、WebSocket 用 DO;无状态、纯计算、转发用普通 Worker。DO 成本更高,不要滥用。

  • DO 的存储有大小限制吗? 单实例存储上限约 1GB(可申请更高)。超过的话考虑分片(多个 DO 实例)或配合 R2。

  • DO 实例会迁移吗? 会。Cloudflare 会根据负载和地理位置自动迁移 DO 实例。迁移过程中会有短暂延迟(毫秒级),但状态不会丢。

  • DO 支持定时任务吗? 支持 state.storage.setAlarm(),可以设置未来的某个时间点触发回调。适合做房间超时清理、游戏回合倒计时。

  • DO 能访问 KV、D1、R2 吗? 可以。DO 的 fetch 方法接收的 env 和普通 Worker 一样,可以绑定各种资源。

错法对照

错法问题正解
在 DO 里做 CPU 密集计算单线程会阻塞所有请求把重计算丢给 Queue 或外部服务
不调用 ws.accept()WebSocket 连接无法建立handleSession 第一件事就是 ws.accept()
用 DO 存大量历史消息单实例存储有限,查询慢历史消息放 D1 或 R2,DO 只存最近状态
期望 DO 自动扩容DO 是单实例,不自动横向扩容分片:按 roomId / userId 创建多个 DO 实例
在 DO 构造函数里做异步操作构造函数不能是 async用 state.blockConcurrencyWhile() 做初始化

卡点急救

  1. idFromName 返回的 ID 无效:确保 class_name 和 wrangler.toml 里的 [[durable_objects.bindings]] 一致;确保类已 export。
  2. WebSocket 连接立刻断开:检查是否调用了 ws.accept();检查 DO 实例是否因为空闲被休眠(用 hibernation API 或定期 ping)。
  3. DO 存储读写慢:持久化存储是磁盘 I/O,比内存慢。高频读写的数据放内存,定期 flush 到 storage。

面试常问

  • Durable Object 的一致性模型是什么? 强一致。单实例单线程,所有请求串行处理,内存状态实时可见。持久化存储通过事务保证原子性。

  • DO 怎么实现分布式锁? 利用单线程语义:所有请求串行执行,天然互斥。不需要额外锁机制。也可以用 storage.transaction() 实现原子操作。

  • DO 和 Redis 怎么选? DO 适合「强一致、低延迟、有状态」的场景(聊天室、游戏、协同编辑);Redis 适合「缓存、简单计数、Pub/Sub」的场景。DO 和 Worker 同节点运行,延迟更低;Redis 需要网络往返。

自测三问

  1. Durable Object 为什么能保证强一致?
  2. 如果聊天室有 10 万用户,你会怎么设计 DO 的架构?
  3. DO 的 state.storage 和 this.xxx 内存变量有什么区别?

小结一句话

Durable Objects 是 Workers 生态中的「强一致状态原语」:全局唯一、单线程、内存 + 持久化,适合聊天室、协同编辑、游戏房间等需要共享状态的场景。

延伸

  • Durable Objects 官方文档
  • Durable Objects Pricing
  • 下一课:Workers AI,学习边缘 AI 推理与向量搜索。
加载本节练习…
← 上一课Queues 消息队列下一课 →Workers AI