ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

自托管Durable Objects方案Celld实测:与Cloudflare原生服务对比解析

自托管Durable Objects方案Celld实测:与Cloudflare原生服务对比解析 在构建现代Web应用时状态管理和数据持久化是开发者面临的核心挑战之一。尤其是在需要跨请求、跨用户会话共享状态或实现实时协作功能的场景下传统的无状态服务器和数据库组合往往显得力不从心。Cloudflare推出的Durable Objects正是为解决此类问题而生它提供了强一致性、低延迟的全局状态存储能力。然而其“绑定”到Cloudflare Workers平台的特性也让许多希望拥有完全控制权或需要在自有基础设施上运行的开发者望而却步。近期一个名为Celld的开源项目进入了我们的视野它宣称能够实现Durable Objects核心概念的“自托管”。本文将深入实测Celld并与原生的Cloudflare Durable Objects进行全方位对比解析为你揭示在自有服务器上构建“状态即服务”的可行性与实践路径。1. 背景与核心概念为什么需要“有状态”的服务器在深入工具之前我们有必要厘清问题的本质。传统的Web服务器如Nginx、Apache和现代的无服务器函数如AWS Lambda、Cloudflare Workers通常被设计为**无状态Stateless**的。这意味着每一次请求的处理都是独立的服务器不会在内存中保留上一次请求的任何信息。状态被外部化到数据库、Redis等存储服务中。这种架构带来了良好的水平扩展性但在某些场景下会引入复杂度和延迟实时协作应用如在线文档、白板需要极低的延迟来同步所有用户的操作状态。游戏服务器需要维护游戏房间的实时状态处理玩家输入并同步给所有参与者。会话密集型服务如购物车、复杂的多步表单频繁读写数据库会成为性能瓶颈。分布式计数器/锁需要强一致性的原子操作而非最终一致性。Cloudflare Durable Objects (DO)正是Cloudflare给出的答案。它本质上是一个全局唯一的、有状态的JavaScript对象运行在Cloudflare的边缘网络上。每个Durable Object都有一个唯一的ID来自全球任何地方的请求都可以通过这个ID找到并与之通信。它保证了强一致性对单个对象的读写是线性的和极低的访问延迟对象在边缘被实例化和运行。那么Celld是什么Celld是一个开源项目其目标是在自托管环境中实现类似Durable Objects的编程模型和核心能力。它允许你在自己的服务器可以是云主机、物理机甚至本地开发机上创建和管理这些“有状态的对象”。这意味着你可以获得DO的开发体验和部分优势而不必绑定在Cloudflare的生态系统中。2. 环境准备与版本说明为了进行公平的实测与对比我们需要搭建两个环境一个是基于Cloudflare Workers Durable Objects的官方环境另一个是自托管的Celld环境。通用基础环境操作系统macOS / Linux (Windows WSL2 也可本文以Ubuntu 22.04为例)Node.jsv18.x 或更高版本 (Celld 和 Wrangler 都依赖较新的Node版本)包管理器npm 或 yarn代码编辑器VS Code 或其他Cloudflare Durable Objects 环境核心工具wrangler- Cloudflare Workers 的官方命令行工具。安装命令npm install -g wrangler账户需求需要一个Cloudflare账户并在wrangler login后完成授权。版本本文使用 Wrangler v3.0.0。Celld 自托管环境核心运行时Deno- Celld 使用 TypeScript 编写运行在 Deno 运行时上。安装Deno(以Linux/macOS为例)curl -fsSL https://deno.land/install.sh | sh # 安装完成后将Deno添加到环境变量根据安装脚本的提示操作 # 验证安装 deno --versionCelld项目我们将直接从GitHub仓库获取并运行示例。版本本文基于 Celld 项目的主分支撰写时的最新提交。3. 核心原理与架构拆解在动手编码前理解两者的工作原理至关重要。3.1 Cloudflare Durable Objects 架构唯一ID与存储绑定每个DO类在wrangler.toml中声明。部署后你可以通过一个由系统生成或自定义的ID来实例化一个对象。边缘执行与迁移DO实例运行在Cloudflare的全球边缘网络上。当某个地理位置的请求需要访问一个DO时系统会尝试在最近的边缘节点激活或迁移该实例以实现低延迟访问。持久化存储DO的内存状态可以通过state.storageAPI持久化到Cloudflare内置的持久化存储中。这保证了即使实例被卸载由于空闲状态也能在下次激活时恢复。基于WebSocket或HTTP的通信DO可以通过HTTP端点或WebSocket连接与客户端通常是Worker进行通信。3.2 Celld 架构中心化协调器CoordinatorCelld架构中有一个核心的Coordinator服务。它负责管理所有Cell相当于DO的生命周期、路由请求到正确的Cell实例所在的Worker进程。工作进程Worker实际执行用户代码你的Cell逻辑的进程。一个Coordinator可以管理多个Worker。Cell用户定义的有状态对象运行在Worker内。每个Cell也有唯一ID。通信协议Celld使用自定义的RPC协议在Coordinator、Worker和客户端之间通信。客户端通常通过一个轻量的SDK来调用Cell的方法。持久化Celld的持久化机制可能依赖于你配置的存储后端如内存、Redis、数据库等这比Cloudflare的封闭存储更灵活但也需要自行维护。关键差异点部署拓扑DO是分布式边缘架构Celld通常是中心化或区域化部署你可以部署多个Coordinator来实现高可用。存储抽象DO提供了集成的、无需操心的持久化Celld需要你选择和集成后端存储。生态系统DO深度集成于Cloudflare Workers生态系统KV, R2, D1等Celld是独立的可与任何后端服务集成。4. 完整实战案例构建一个简单的计数器服务我们将通过一个经典的“分布式计数器”例子来对比两者的开发体验。这个计数器可以被多个客户端并发地增加、减少和读取并保证值的正确性。4.1 使用 Cloudflare Durable Objects 实现第一步创建项目并初始化# 创建一个新的Workers项目 mkdir cf-do-counter cd cf-do-counter npm create cloudflarelatest . -- --typehello-world # 安装完成后初始化wrangler配置 npx wrangler init第二步定义Durable Object类创建文件src/counter.js// 文件src/counter.js export class Counter { constructor(state, env) { this.state state; // 从持久化存储中初始化计数器默认为0 this.state.blockConcurrencyWhile(async () { this.value (await this.state.storage.get(value)) || 0; }); } // 处理HTTP请求 async fetch(request) { const url new URL(request.url); switch (url.pathname) { case /increment: this.value; await this.state.storage.put(value, this.value); return new Response(this.value.toString()); case /decrement: this.value--; await this.state.storage.put(value, this.value); return new Response(this.value.toString()); case /: return new Response(this.value.toString()); default: return new Response(Not found, { status: 404 }); } } }第三步配置 wrangler.toml编辑wrangler.toml文件name cf-do-counter compatibility_date 2024-01-01 [[durable_objects.bindings]] name COUNTER # 在Worker中使用的变量名 class_name Counter # 对应的DO类名 [[migrations]] tag v1 new_classes [Counter] # 声明要创建的DO类第四步编写调用DO的Worker编辑src/index.js// 文件src/index.js export default { async fetch(request, env, ctx) { // 根据请求路径获取一个唯一的Counter实例ID // 例如所有请求共享一个计数器我们使用固定ID global” const id env.COUNTER.idFromName(global); const obj env.COUNTER.get(id); // 将请求转发给Durable Object实例处理 return obj.fetch(request); }, };第五步本地测试与部署# 本地开发 npx wrangler dev # 部署到Cloudflare npx wrangler deploy部署后你将获得一个.workers.dev的域名。访问https://your-project.your-account.workers.dev/查看当前值访问/increment和/decrement路径来修改值。4.2 使用 Celld 自托管实现第一步获取Celld示例代码git clone https://github.com/celldev/celld.git cd celld/examples # Celld的示例通常在examples目录下我们找一个简单的示例作为基础进行修改。假设有一个basic-counter例子。第二步理解Celld项目结构一个典型的Celld项目包含coordinator.ts: 协调器服务入口。worker.ts: 工作进程入口加载用户定义的Cell。cells/: 目录存放用户定义的Cell类我们的业务逻辑。client.ts: 用于测试或外部调用的客户端。第三步编写自定义Counter Cell在cells/目录下创建counter.ts// 文件cells/counter.ts import { Cell } from “https://deno.land/x/celld/mod.ts”; export class CounterCell extends Cell { private count: number 0; // 初始化方法可能从持久化中加载数据 async onStart() { // 这里可以连接数据库或Redis加载初始状态 // this.count await this.loadFromStorage(); console.log(CounterCell ${this.id} started.); } // 定义对外暴露的RPC方法 async increment(): Promisenumber { this.count; // await this.saveToStorage(this.count); // 持久化 return this.count; } async decrement(): Promisenumber { this.count--; // await this.saveToStorage(this.count); return this.count; } async getValue(): Promisenumber { return this.count; } }第四步修改Worker以注册我们的Cell编辑worker.ts// 文件worker.ts import { Worker } from “https://deno.land/x/celld/mod.ts”; import { CounterCell } from “./cells/counter.ts”; const worker new Worker(); // 注册Cell类型 worker.registerCell(“counter”, CounterCell); // 启动Worker连接到Coordinator await worker.start({ coordinatorUrl: “ws://localhost:8080”, // Coordinator的地址 workerId: “worker-1”, });第五步启动Coordinator和Worker首先启动Coordinator在项目根目录deno run --allow-net --allow-read coordinator.ts然后在另一个终端启动Workerdeno run --allow-net --allow-read worker.ts第六步编写客户端进行测试创建test_client.ts// 文件test_client.ts import { Client } from “https://deno.land/x/celld/mod.ts”; const client new Client(“ws://localhost:8080”); await client.connect(); // 获取或创建一个ID为 “global-counter” 的CounterCell实例 const counter await client.getCell(“counter”, “global-counter”); console.log(await counter.call(“getValue”)); // 0 console.log(await counter.call(“increment”)); // 1 console.log(await counter.call(“increment”)); // 2 console.log(await counter.call(“decrement”)); // 1 await client.disconnect();运行客户端deno run --allow-net test_client.ts5. 实测对比与深度解析通过上面的实践我们可以从多个维度进行对比特性维度Cloudflare Durable ObjectsCelld (自托管)部署与运维完全托管无需管理服务器、运行时和扩缩容。自行负责需要部署、监控、维护Coordinator和Worker进程考虑高可用和备份。性能与延迟全球边缘低延迟实例可迁移到用户附近。性能由Cloud保障。取决于部署位置。如果部署在单一区域远端用户延迟高。需自行设计多地部署方案。持久化存储内置、透明使用state.storageAPI无需关心底层细节。需自行集成灵活性高可选Redis、PostgreSQL等但复杂度和责任转移给开发者。开发体验与生态高度集成与Wrangler CLI、Workers调试工具、Cloudflare仪表板无缝结合。文档和社区成熟。相对原始依赖Deno生态工具链和调试体验正在发展中。需要更多手动配置。成本模型按请求和GB-秒计费有免费额度。对于突发流量成本可能随用量增长。主要是基础设施成本服务器/虚拟机费用。流量固定时成本可能更可控但需要预留资源应对峰值。锁定与可移植性高供应商锁定代码和架构深度绑定Cloudflare。低锁定代码可在任何能运行Deno的环境尝试运行但对Celld运行时本身有依赖。适用场景1. 全球分布的实时应用。2. 希望极致简化运维。3. 项目已在Cloudflare生态内。1. 对数据主权和隐私有极高要求。2. 已有基础设施希望复用。3. 作为学习DO概念的实验性平台。4. 云成本优化是首要考虑。Celld当前的主要挑战基于实测生产就绪度作为一个新兴开源项目其稳定性、性能极限、故障恢复机制需要更多生产环境检验。运维复杂度你需要成为Celld系统的运维者包括Coordinator的高可用、Worker的扩缩容、持久化存储的维护和监控。功能完整性Cloudflare DO经过多年发展拥有丰富的功能如WebSocket Hibernation、Alarms、Transactional Storage API。Celld可能尚未实现所有高级特性。社区与支持遇到问题时你主要依靠开源社区和代码自查而非商业技术支持。6. 常见问题与排查思路在开发和运行过程中你可能会遇到以下问题问题现象可能原因 (Cloudflare DO)可能原因 (Celld)解决思路本地开发正常部署失败wrangler.toml配置错误DO类名未在migrations中声明账户权限不足。Coordinator与Worker网络不通Deno权限不足如--allow-netCell类注册名称不匹配。CF DO检查wrangler.toml语法运行wrangler publish --dry-run验证。Celld检查Coordinator日志确保Worker连接URL正确使用deno run时赋予足够权限。状态丢失未正确使用state.storageAPI进行持久化blockConcurrencyWhile使用不当导致竞态条件。未实现持久化逻辑Worker重启后状态丢失持久化后端如Redis连接失败。CF DO确保所有状态变更都通过storage.put保存。Celld在Cell的onStart中加载状态在方法中保存状态并做好存储后端的错误处理。高延迟DO实例可能被迁移或冷启动首次请求延迟高。Coordinator和Worker部署区域与用户距离远网络带宽不足。CF DO考虑使用alarms保持实例活跃。Celld将服务部署在离用户更近的区域或部署多个地理分布的Celld集群。“Cell not found” 或 “DO inaccessible”DO ID生成或解析逻辑错误该DO实例尚未被创建。Cell ID错误持有该Cell的Worker进程已崩溃且未恢复。CF DO检查idFromName或idFromString的逻辑。Celld检查Coordinator的路由表确认Worker健康状态实现Cell状态的持久化以保证可恢复。内存使用量持续增长DO实例中积累了未释放的全局变量或缓存存在内存泄漏。Cell内存在内存泄漏Worker进程未及时清理已销毁的Cell实例。使用开发工具进行内存分析。对于Celld需要监控Worker进程的内存并可能需实现定期的清理或重启策略。7. 最佳实践与工程建议无论选择哪种方案遵循一些最佳实践都能让你的有状态服务更加健壮。通用建议精心设计IDDO/Cell的ID决定了对象的粒度。过于细粒度如每个用户一个可能导致实例爆炸过于粗粒度如全局一个可能成为性能瓶颈。根据业务访问模式设计。拥抱幂等性尽管DO/Cell提供了强一致性但在网络重试等场景下让操作尽可能幂等可以简化客户端逻辑。超时与重试客户端调用必须设置合理的超时和重试策略以应对网络波动或实例冷启动。监控与告警必须监控请求延迟、错误率、实例数量、内存使用等关键指标。对于Celld还需监控底层服务器资源。Cloudflare Durable Objects 特定建议合理使用blockConcurrencyWhile这个方法是保证状态线性的关键但过度使用会阻塞并发请求。只在读取-修改-保存状态的临界区使用它。利用Alarms进行定期任务DO支持定时任务Alarms可用于清理过期数据、发送定期心跳等。规划Durable Object迁移当代码发生变更时需要仔细规划migrations特别是存储格式变化时。Celld 自托管特定建议生产部署架构至少部署两个Coordinator实例以实现高可用前面用负载均衡器如Nginx做代理。Worker进程可以水平扩展。持久化存储选型对于生产环境必须集成外部持久化存储如Redis。并做好备份和灾难恢复方案。安全加固Celld服务通常监听内部网络确保防火墙规则正确避免暴露到公网。考虑在RPC通信层增加认证和加密TLS。资源限制与隔离为每个Worker进程设置内存和CPU限制例如使用Docker防止单个异常的Cell拖垮整个Worker。日志与追踪在Cell代码中植入详细的日志并统一收集到ELK或类似系统中这对于排查分布式状态问题至关重要。8. 总结与选型指南Cloudflare Durable Objects 和 Celld 代表了实现“有状态后端”的两种不同哲学一个是全托管的云原生服务另一个是赋予开发者完全控制权的自托管方案。如何选择选择 Cloudflare Durable Objects如果你追求极致的开发效率和运维简便性应用用户全球分布对延迟敏感项目规模适中且愿意拥抱Cloudflare生态系统团队不希望管理服务器基础设施。选择 Celld 或类似自托管方案如果你对数据隐私、合规性和主权有强制要求已有成熟的基础设施团队和运维体系希望长期成本更可控固定流量下技术栈需要深度定制或与现有系统集成或者你正在深入研究分布式有状态系统的原理将其作为一个优秀的学习和实验平台。学习路线建议入门理解首先通过Cloudflare的官方文档和免费额度亲手体验Durable Objects理解其编程模型和核心价值。原理深入阅读Celld的源代码理解Coordinator、Worker、Cell之间的通信机制和生命周期管理这能加深你对分布式状态管理的理解。原型验证用Celld搭建一个原型模拟你的业务场景重点测试其持久化、故障恢复和扩展能力。生产规划如果决定采用自托管方案必须详细规划监控、告警、备份、升级和灾难恢复流程这往往是项目成功的关键。自托管从来都不是一条更轻松的路它用运维的复杂性换来了控制的自由度。Celld项目为我们在Cloudflare生态系统之外打开了一扇窗让我们看到了“Durable Objects”模式更通用的未来可能性。无论你最终选择哪条路径理解状态管理的核心挑战与解决方案都将是你架构能力的一次重要提升。
返回列表