Introducing on-demand CPU and memory profiling with flamegraphs for Workers and Durable Objects
TL;DR · AI 摘要
Cloudflare 为 Workers 和 Durable Objects 推出按需 CPU/内存火焰图分析,可精准定位性能瓶颈。
核心要点
- 使用 cf CLI 工具可执行 workers versions profile 命令生成性能分析文件
- 火焰图中矩形宽度直接反映函数 CPU 时间或内存占用比例
- TypeScript 项目必须启用 source maps 否则函数名将被混淆
结构提纲
按章节快速跳转。
- §引言
生产环境性能分析面临定位困难,传统日志和指标难以精准定位问题根源。
- ·功能发布
Cloudflare 官方宣布支持 Workers 和 Durable Objects 的按需 CPU/内存分析功能。
通过 cf CLI 工具可执行 workers versions profile 命令生成性能分析文件。
Cloudflare Dashboard 提供交互式火焰图分析界面,支持版本切换和持续时间设置。
需确保 Worker 有足够的流量,TypeScript 项目必须启用 source maps。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- Cloudflare 性能分析功能
- 功能特性
- CPU/内存分析
- 火焰图可视化
- 使用方式
- CLI 工具
- Dashboard 界面
- 注意事项
- 流量要求
- Source maps 配置
金句 / Highlights
值得收藏与分享的关键句。
火焰图中每个矩形宽度直接对应函数的 CPU 时间或内存占用比例
生产环境 profiling 是理解应用行为的最佳方式
未启用 source maps 的 TypeScript 项目将显示混淆函数名
为 Workers 和 Durable Objects 引入按需 CPU 和内存分析(火焰图) | Cloudflare 博客
post
Serverless
Workers
开发者
可观测性
性能
2026年10月9日
为 Workers 和 Durable Objects 引入按需 CPU 和内存分析(火焰图)
Dominik Picheta
10 分钟阅读
复制链接
理解为什么应用程序使用的资源(无论是 CPU 还是内存)超出预期可能颇具挑战性。日志和聚合指标只能带您走这么远。幸运的是,有一种更好的方法:通过 CPU 或内存分析,您可以精确查看应用程序中消耗 CPU 或分配内存的具体函数。
今天,我们很高兴宣布支持对 Workers 和 Durable Objects 进行 CPU 和内存分析。现在,您可以通过 Workers 可观测性页面,请求某个活跃 Worker 的按需 CPU 或内存分析,以交互式火焰图形式查看分析结果,并下载分析文件进行进一步分析。
这种分析方法能为您提供有价值的视角,帮助您了解代码的实际运行情况,并在真实环境中找到改进方向。这是因为了解应用程序的最佳方式,是在生产环境中对其进行分析。
要尝试在您的 Worker 上使用此功能,您可以通过 CLI 或 Cloudflare 控制台进行操作。
使用 CLI 时,请确保已安装 cf 包,然后只需运行以下命令:
cf
workers
versions
profile
latest
\
     --worker-id
"
$WORKER_ID_OR_NAME
"
\
     --duration-ms
5000
\
     --profile-type
cpu
>
worker-cpu.pprof使用控制台时,请前往 Cloudflare 控制台,通过 Build → Compute → Workers & Pages 路径访问您账户中的 Worker 列表。
选择您的 Worker 并进入其可观测性标签页。然后可以通过下拉菜单选择“火焰图”:
在此页面上,您可以为 Worker 请求 CPU 和内存分析。持续时间决定了分析在 Worker 上运行的时间长度。您还可以选择不同版本的 Worker 进行分析。为了确保分析成功,您的 Worker 需要足够的流量,因此请选择一个流量充足的版本。
点击“捕获分析”按钮后,分析器将运行您选择的持续时间,随后您将看到一个火焰图,展示分析结果。火焰图中的每个矩形代表一个函数调用,宽度表示该函数消耗的 CPU 时间或内存大小。您可以点击不同函数以聚焦查看,悬停时可查看更详细信息:
不要害怕捕获几个分析结果,然后寻找最宽的函数以确定哪些函数在您的 Worker 中消耗了最多资源。您可能还会发现表格视图有助于快速了解哪些函数在分析中最为常见。
如果您的 Worker 是用 TypeScript 实现的,请确保已为项目启用源映射,否则您的分析结果可能会显示难以理解的模糊函数名称。
我们已经在 Cloudflare 多个团队中看到,通过 CPU 和内存分析识别出优化内存和 CPU 使用的机会,修复了内存不足(OOM)错误,并提升了性能。以下是几个示例:
使用 CPU 分析寻找优化机会
我们来看一个实际案例,了解如何通过 CPU 分析来优化 Workers。我们将分析一个实现 R2 绑定的 Worker 的 CPU 分析结果。这个 Worker 接收大量流量,因此即使消除最微小的 CPU 资源浪费,也能对其性能产生巨大影响。
我们配置了分析器来捕获持续 50 秒的 CPU 追踪记录。获取到渲染后的分析结果后,我们看到了以下情况:
如前所述,最宽的方框表示占用最多 CPU 时间的部分。其中一些并不令人意外,例如 decryptBlock 是 R2 在传出数据时解密对象数据,fillResponse 只是移动字节。这些函数并没有明显的优化方式。
由于部分方框的可见性较低,查看表格视图可能是个好主意。按样本数排序后,我们注意到 genericR2JsonReplacer:
这个函数占用了超过 5% 的 CPU 时间,但引起我们注意的是该函数递归调用自身的现象。这看起来像是浪费的工作,事实也确实如此。
尽管 replacer 被 JSON.stringify 在 JSON 树的每个节点上调用,但 replacer 本身也在遍历树。这意味着嵌套五层的值会被处理五次。修复这个问题后,我们消除了重复工作,使 genericR2JsonReplacer 函数的性能提升了 2.7 倍。
我们在分析中看到的第二个候选优化点是 metrics 的重复调用。
查看代码时,我们发现了类似以下的代码:
function
ship
() {
if
(
!
registry.
metrics
().
length
) {
return
;
}
// ... more code here ...
return
Response
(registry.
metrics
());
}metrics 调用的开销足够大,单次调用就占用了分析中 1% 的 CPU 时间。因此,将第一次调用的结果存储在变量中并避免重复调用,为我们节省了大量 CPU 时间。
我们如何通过 Worker 分析内部解决内存问题
我们内部有一个 Worker 存在内存问题。P999 内存使用量在 133 MB 左右,超过了 128 MB 的 Worker 内存限制。这导致 Worker 频繁被驱逐,并出现 "Exceeded Memory" 错误。
上图显示了 "Errors by invocation status" 图表的截图,通过分析发现的修复措施使 "Exceeded Memory" 错误数量明显下降。
困难的是找出导致这些错误的原因。错误和指标都指向内存是问题的根源,但并未说明代码的哪个部分导致了这个问题。
团队对生产环境中运行的其中一个 Worker 进行了堆分析,并使用 pprof 打开分析结果。他们发现 Prometheus 代码在分析中占用了约 66.7% 的内存分配。这段代码本应被禁用,但仍有部分代码在运行,导致了问题。由于它对 Worker 的许多代码路径进行了监控,即使收集的数据从未离开 Worker,也导致了大量内存使用。
这段代码原本未被怀疑是内存问题的元凶,因为人们认为它已经被禁用了。但分析器显示代码仅被部分禁用,内存成本仍然像完全启用时一样被计算。
团队彻底移除了 Prometheus 代码路径。这迅速改善了 Worker 的内存使用情况:
百分位数
之前(MB)
之后(MB)
P50
70
54
P90
94
79
P99
113
97
P999
133
118
这使 P999 在 128 MB 限制下获得了约 10 MB 的余量。
跟踪边缘的性能分析请求
Workers 已经通过 Chrome DevTools 支持本地性能分析一段时间了,但此前无法在生产环境中启动 Workers 的性能分析会话。虽然本地分析对 Workers 有帮助,但实际运行时收到的请求类型和数量与生产环境存在差异。那么,我们如何对正在运行的 Worker 或 Durable Object 进行性能分析呢?
Workers 的一个显著优势是其请求会被路由到距离请求客户端最近的数据中心。这降低了延迟,但也意味着 Workers 必须在不同数据中心和物理服务器(metal)之间进行复制。此外,当某个数据中心的 Worker 收到大量请求时,系统可能会在该物理服务器上多次复制该 Worker 以处理所有流量。
Durable Objects 增加了另一层间接性:它们可以动态放置并迁移。
这些特性以及其他一些 Workers 的特性意味着,在启动性能分析会话之前,我们需要先回答几个问题:
- 开发者希望分析哪个版本?
- 该版本最近在哪个数据中心运行过?
- 该数据中心是否仍然加载了 Worker 的隔离实例?
- 该隔离实例是否专属于请求账户?
- 对于 Durable Object,精确的主实例位于何处?
这些问题中的一部分由发起性能分析请求的客户端来回答。大多数情况下,这将是仪表板,但你也可以自行发送这些请求(或让你的编码代理代为执行)。一个示例请求可能如下所示:
curl 'https://api.cloudflare.com/client/v4/accounts/<account_id>/workers/workers/<worker_name>/versions/latest/profile' \
--data-raw '{"duration_ms":5000,"profile_type":"cpu"}'值得注意的是,请求 URL 中明确指定了要分析的脚本名称和版本。
目前,为了观察真实的生产执行情况,不会启动新的隔离实例来生成性能分析。这意味着如果你的 Worker 接收到的流量非常少,可能会发现难以进行分析。因此,选择一个已部署的 Worker 版本非常重要。
在不停止 Worker 的情况下对隔离实例进行性能分析
只有在应用程序能够继续执行的同时收集样本时,性能分析才有意义。因此,Workers 运行时仅在性能分析生命周期操作期间持有隔离实例的锁。对于 CPU 性能分析,流程如下:
- 获取隔离实例及其锁。
- 创建 V8 CPU 性能分析器,并以 1 毫秒的间隔开始采样。
- 释放锁以允许正常请求运行。
- 等待请求的持续时间。
- 重新获取锁并停止分析。
- 释放锁并在非关键区域序列化结果。
在整个性能分析过程中持有锁会阻止 JavaScript 执行继续,从而使性能分析变得毫无意义。
Durable Objects
持久对象(Durable Objects)与 Workers 有本质区别。常规的 Workers 是无状态的:所有 Workers 之间没有特殊性或差异,我们随机选择一个正在运行的隔离实例来处理请求。而持久对象是有状态的,且拥有名称。这使我们能够在全球任何地方获取特定隔离实例和特定对象的配置文件。在对持久对象进行配置分析时,你可以通过名称选择要分析的对象。运行时会将分析请求路由到拥有该对象的特定金属节点,从而提供该对象当前运行的隔离实例的配置文件。
下一步是什么?
按需分析只是起点。它使你能够以前所未有的方式探索 Workers 的性能表现。但它也有一些需要注意的限制:
- 你必须显式启动分析会话。这意味着你可能会错过 Workers 行为异常的重要时间段,而你本希望看到分析结果来了解问题所在。
- 内存分析器只能显示分析窗口期间发生的内存分配。因此,如果 Workers 在启动期间有大量内存分配,你将无法看到这些数据。
为了解决这些问题,我们正在开发一项新功能:持续分析。其核心思想是自动捕获 Workers 的分析样本,这样你只需在仪表板中查看分析结果即可。这应该能更轻松地捕获罕见事件。
要了解更多关于 Workers 分析的信息,包括本文介绍的功能,请查看我们的文档。
特别感谢 Workers 控制平面团队和 Cloudflare 可观测性平台团队,特别是 William Perron,他为本项目仪表板的实现提供了帮助。
目录列
讨论列
相关标签和社交媒体链接
相关标签
关注社交媒体
- Cloudflare
- Dominik Picheta
电子邮件订阅