综合 Deno 作者开源 celld:放弃 Raft,把 S3 当持久化底座跑有状态协同

2026-09-11 23:21:17

Deno 作者开源 celld:放弃 Raft,把 S3 当持久化底座跑有状态协同

前两天跟朋友聊实时协同画板,大家吐槽最多的还是服务端状态到底怎么存。

按常规思路搞一套无状态服务加集中式数据库,光是每秒同步几十个用户的鼠标拖拽轨迹和画笔坐标,数据库连接池就得天天告警,行锁更是卡得要命。Cloudflare 的 Durable Objects 确实把这种有状态协同计算封装得非常舒服,但代码全得绑在他们家的闭源云平台上,一旦公司有私有化部署、合规审计或者多云容灾的要求,立马就抓瞎了。

Deno 的作者 Ryan Dahl 最近开源的 celld 项目,专门来解决这个自托管的麻烦事。

他的实现路径是:不去折腾 Raft 或者 Paxos 这类分布式共识集群,而是直接把普通的对象存储(比如 AWS S3 或者 MinIO)当成持久化底座,跑通高可用、强一致的有状态协同。连 Durable Objects 的原作者 Kenton Varda 看到之后,也专门跑去社区参与了讨论,认可这种方案在自托管场景下的取舍。

每个协同单元,都是一个带 SQLite 的轻量 V8 实例

以前搞协作系统,最折腾的地方在于每次客户端发个消息过来,服务端通常都要先去数据库把整张画板的状态捞出来,在内存里算完差异,再写回库里、顺带发个 Redis 广播。整个流程在网络和序列化上多绕了好几圈,延迟压根下不来。

celld 的思路很直接:别每次都去查远程数据库了,直接把每一个画板房间或者每一局游戏,在内存里起一个独立的「Cell」常驻着。

每个 Cell 底层就是一个轻量的 V8 Isolate 沙箱,而且实例内部直接塞进了一个专属于该对象的轻量 SQLite 数据库。

客户端发过来的 WebSocket 消息或者 RPC 调用,网关会就近打进负责该 Cell 的内存实例。因为改动只写当前进程的内存与本地 SQLite,改完顺手就能把结果广播给房间里的其他连接。没有跨网络查库的拖累,单次操作的响应延迟可以压进微秒级。

没有复杂的分布式共识,数据怎么做到不丢?

做有状态计算最棘手的地方,从来不是内存计算有多快,而是物理节点的容灾与故障转移(Failover):如果某台跑着 Cell 对象的物理服务器突然断电或者宕机了,内存里的状态和本地的 SQLite 数据怎么迁移到新机器上?

过去为了保证数据不丢,传统的分布式数据库通常需要为每个分片维护 3 到 5 个副本,依靠复杂的 Raft 或 Paxos 协议在多个节点之间实时同步状态机日志。这套架构不仅非常吃机器资源,后期的运维排错成本对于中小团队来说也基本是一道很难跨过去的门槛。

celld 取巧的地方在于,它直接借用了 Litestream 那套 LTX(Log Transaction File)事务日志协议,把最难搞的一致性和容灾全甩给了 S3:

  • 数据异步落盘。Cell 内嵌的 SQLite 本地每提交一笔事务,底层的日志流收集器就会把增量的 WAL 日志切成极小的 LTX 文件块,然后以非阻塞流的方式异步推送到底层的 S3 存储桶里,完全不卡本地的内存读写。
  • 靠 S3 搞定分布式租约(Lease)。它直接利用 S3 对象的条件写入(Conditional Put,也就是 If-None-Match 机制)来管理 Cell 归属权。只要当前机器正常跑着就定期刷新租约;一旦物理机失联或者宕机,租约超时之后,调度层立马就会在新机器上接管这个对象。
  • 新节点的极速重放。新机器抢到对象的控制权后,直接从 S3 把最新的 LTX 事务日志拉下来,毫秒级就能在本地把 SQLite 数据库完整重放还原出来,顺带拉起 V8 实例恢复协同服务。

这么一套组合拳下来,整个系统根本不需要维护任何复杂的共识选举集群,廉价且具备无限容量的标准 S3 存储桶,就成了系统唯一且绝对可靠的持久化事实源。

// 定义一个自托管的协同白板对象
import { Cell } from 'celld';

export class WhiteboardSession extends Cell {
  // 每个协同房间独占一个内嵌的 SQLite 数据库
  async onStroke(userId: string, pathData: string) {
    await this.db.execute(
      'INSERT INTO strokes (user_id, path) VALUES (?, ?)',
      [userId, pathData]
    );
    // 直接在内存中向当前房间的所有在线连接广播,零外部数据库依赖
    this.broadcast({ type: 'draw', userId, pathData });
  }
}

适用边界与技术权衡

这套架构也不是万能的银弹,选型前有两点必须心里有数:

一个是它只适合「以单个实体为边界」的高频状态读写,比如在线文档协同、画板房间、多人即时游戏或者智能体的长会话状态。但如果业务场景需要跨成千上万个对象做复杂的全局 SQL 联表查询或者跑 OLAP 报表分析,就别这么干,这种需求还是得靠异步 ETL 管道把数据汇总到专门的数据仓库里。

另一个是故障转移的秒级抖动。因为对象归属权靠 S3 的租约机制来检测,万一遇到极端网络分区或者物理机突然宕机,新节点从抢到租约到拉起 SQLite 实例,中间大概会有 1 到 2 秒的恢复收敛窗口。

但对不想被云厂商专有平台绑死、又想在自建 Kubernetes 或普通 VPS 上搞定低延迟协同的团队来说,用 S3 代替沉重的分布式共识集群,确实是一个把运维心智和架构复杂度降到极低的好思路。

#分布式系统 #Deno #架构设计 #开源软件

复制全文 生成海报 分布式系统 Deno 架构设计 开源软件

推荐文章

程序员茄子在线接单