在本地使用 [Cloudflare Workers43 进行开发是当今无服务器提供的最流畅的体验之一。 0 在几秒钟内上升,日志出现在终端中,一切似乎都按照你的预期运行。问题在于,这种安慰掩盖了只有当代码真正到达边缘时才会出现的根本差异。许多团队后来发现,除非有人主动执行 2 ,否则生产中的 1 毫无用处,缓冲 50MB 响应会默默地杀死隔离,并且在 D1 中执行 15 次查询加上在 KV 中执行 10 次读取的处理程序是针对子请求限制的定时炸弹。
生产中的日志会发生什么情况
在本地,3每4都会实时显示在终端上。在生产中,Cloudflare 的 V8 隔离没有持久的过程来捕获此输出 - 每个调用都是隔离运行、执行并消失。日志只能通过 5 访问,它会打开与生产中的 Worker 的流会话,并中继请求元数据、6 输出、未捕获的异常和 CPU 持续时间。
关键点:这个会话不持久。如果发生错误时没有人跟踪,日志就会丢失。对于保留,解决方案是 Logpush,它将日志导出到 R2、S3、Datadog 或 Splunk,R2 的每百万行 0.05 美元。但 Logpush 使用的是 Worker 运行时发出的结构化字段,而不是 7° 自由文本。实际结果是,生产中有用的日志需要从一开始就结构化:带有 8 、 9 、 10 、 11 等字段的 JSON - 12 可以过滤并且 Logpush 可以忠实导出的字段。
有效的模式是在项目开始时、在您需要它之前创建一个最小的日志记录包装器。序列化为 JSON 并以 13 写入的内容,其中 14 字段用于区分 15 和 16。当 Logpush 出现时,这些字段可用于目标上的过滤器和警报。
内存:128MB 比看上去要小
每个隔离的 128MB 限制包括 V8 中未压缩的脚本、所有模块闭包以及当前调用的整个堆。它不是“您的数据”的 128MB — 它是所有内容的 128MB,包括运行时本身。
最常见的错误是在较大的答案中调用 17。从另一个服务下载的 30MB 文件要进行处理和转发,会立即占用 30MB 的堆。如果处理创建更多中间分配,隔离超过 128MB 并被终止 - 没有可捕获的异常,没有对客户端的响应,只是外部出现 1101 错误。
解决方案是将18用作19并通过20进行处理。您不需要缓冲和转换,而是创建一个块流动的管道:从上游读取,在传输中转换,写入响应,而内存中不存在整数。对于大于 1MB 的响应,假设应该是流式的;缓冲应该是一个明确且合理的选择,而不是默认路径。
同样的道理也适用于上传。最大请求正文为 100MB,但 21 尝试一次分配所有内容。对于大型上传,处理也必须在流中完成,或者文件必须通过接受 23 的 22 直接发送到 R2。
子请求:最坏时候出现的限制
每个24、KV 上的每个操作、D1 上的每个查询、R2 上的每个读取都算作一个子请求。在付费计划中,每次调用的限制为 1000。免费,50。
看起来合理的处理程序 — 在 D1 中获取用户,在 KV 中读取他们的首选项,从 R2 中提取文档,调用外部 API,将结果保存在 D1 中 — 已经将 5 个子请求放入快乐路由中。如果此处理程序因为处理项目列表而进入循环,则计数器会快速增加。 50 个项目,每个项目有两个操作,已经接近 100。在 D1 中进行 N+1 查询的错误(单独搜索每个子记录而不是使用 JOIN)在单个复杂请求结束之前很容易溢出 1000。
达到限制时的错误并不明显:Worker 在超出限制的子请求上收到网络错误,这可能与网络或下游服务的不稳定相混淆。正确的诊断需要通过 25 查看异常日志并与处理程序调用模式相关联。
缓解措施:在 D1 中使用 26 将多个查询分组为单个子请求。在模块初始化时读取一次 KV 配置并在全局范围内缓存 — 隔离可以在同一 PoP 中的请求之间重用,并且只要隔离存在,第一次调用中发生的 KV 读取就不需要在后续调用中重复。
秘密、环境和进入存储库的 wrangler.toml
在 28 下的 27 中声明的变量是配置文件中的明文,通常会进入存储库。对于任何敏感值(API 密钥、银行令牌、webhook 机密),唯一正确的选项是 Workers Secrets,在 wrangler.toml 中声明为 29 并通过 30 存储。该值在静态时进行加密,并在运行时作为 31 的属性注入,不会出现在任何构建日志或工件中。
投入生产的服务的 wrangler.toml 必须具有显式环境:32和 33 具有自己单独的绑定、路由和机密。在同一组绑定中混合登台和生产是一个随时可能发生的事故,尤其是当 D1 和 KV 在生产中拥有真实数据而在登台中有测试数据时。 wrangler.toml 中的分离环境还允许 CI 管道自动部署到临时环境,并需要手动批准才能进行生产,而无需在部署脚本中添加任何额外逻辑。
最低生产设置是什么样的
生产就绪的34显式引用35(以免在没有警告的情况下从运行时接收重大更改),用37定义3637以在仪表板上公开基本指标,并使用不同的路线将3839分开。秘密按名称列出,没有值 - 该值仅存在于 Cloudflare 中,从不存在于存储库中。
主处理程序在最高级别有一个 40 ,它捕获任何未处理的异常,将其记录为 41 结构的 JSON,并返回带有可跟踪的 42 的 HTTP 500 响应。如果没有这个边界,意外的异常可能会向客户端返回带有截断正文的 200,而真正的错误仅出现在尾部 - 并且仅当有人查看时才出现。
另请阅读
- [Cloudflare Workers:无服务器边缘计算实用指南44
- [生产中的 D1:性能、限制以及无法单独扩展的内容45
- [WebAssembly 处于边缘:为什么快速启动和隔离很重要46
- [Workers:调试、日志和 Workers Tail — 无需日志服务器即可在边缘进行观察47
- [工人:CPU 和内存限制 - 文档没有很好地解释48
- [2025 年使用 AWS Lambda 和 Cloudflare Workers 开发无服务器应用程序49
