Cloudflare 上线生产火焰图:Workers 的 CPU 热点和内存分配该怎样查

Cloudflare 于 2026 年 10 月 9 日推出 Workers 和 Durable Objects 的按需 CPU、内存分析。开发者可以在控制台请求一次采集,查看交互式火焰图,也能下载 profile 文件进一步分析。

这给生产排障补上了更具体的证据:CPU 都花在什么调用链里,哪些代码正在大量分配内存。不过,Heap profile 并不是存活对象快照,按需采集也不会还原过去的请求。正确选择版本、实例和采集窗口,比拿到一张火焰图后立刻优化最宽的函数更重要。

日志说明发生了什么,采样帮助定位资源花在哪里

一次请求超过预期耗时,可能是计算昂贵,也可能是在等待网络或存储。看到内存错误,同样不代表已经知道是哪个模块造成的。日志和聚合指标可以指出现象,但通常不能直接给出函数级资源分布。

这次发布提供两种 profile。CPU profile 用于识别消耗 CPU 时间的函数;控制台中的 Heap profile 则记录采集期间的内存分配。官方文档说明,后者每分配 512 kB 内存采样一次调用栈。

诊断输入 适合回答的问题 不能据此直接得出的结论
请求日志和聚合指标 哪些请求失败、何时异常、资源走势怎样 哪个函数承担了全部资源开销
CPU profile 采集期间哪些调用链占用较多 CPU 整个请求的等待时间都由这些函数造成
Heap profile 采集期间哪些调用链分配了较多内存 这些内存仍然存活,或已经证明存在泄漏

排障时,这些证据应当互相补充。比如某个接口延迟高,但 CPU profile 没有明显热点,就应继续检查等待和依赖服务,而不能只在 JavaScript 函数里寻找答案。

采集已有实例,不会替你启动一次请求

Workers 在不同数据中心和物理服务器上运行。即使同一个版本,也可能存在多个运行实例;Durable Objects 还涉及有状态对象的定位和迁移。

官方公告描述的采集流程,需要先找到最近运行过目标版本的位置,并确认 isolate 仍然加载。普通 Worker 会选择一个正在运行的 isolate,而不是把所有地区、所有副本汇成一张全局 profile。Durable Object 则可以指定目标实例,将请求路由到对应的运行位置。

这决定了结果的代表性。一次采集里的热点,可能反映该实例在当时处理的流量,不应直接推成整个应用在所有地区的固定比例。多次采集、记录版本和业务场景,通常比只看一张图更有帮助。

平台目前不会为了生成 profile 启动一个新 isolate,也不会因为点击采集按钮就主动调用业务代码。目标 Worker 或 Durable Object 需要在采集前和采集期间有流量。测试环境若没有请求,应先准备能够覆盖相关代码的受控请求;生产环境则要确认选中的版本确实正在服务业务。

CPU 采样期间正常请求仍能运行

Cloudflare 公告解释了 CPU 采集的实现过程:运行时取得 isolate 及其锁,创建 V8 CPU profiler,并以一毫秒间隔开始采样;随后释放锁,让正常请求继续执行。到达指定时长后,再取得锁停止采样,并在临界区之外序列化结果。

这个设计避免在整个采集窗口内占着 isolate 锁。否则,JavaScript 无法继续运行,拿到的结果也就失去了观察真实执行的价值。

但“采集期间继续服务请求”不等于“分析工具零开销”。本文引用的资料没有给出适用于所有业务的统一开销比例,采用前仍应观察代表性负载下的表现,并控制采集频率。

火焰图的宽度不是时间轴

控制台中的矩形表示采样调用栈里的函数,宽度按 profile 类型表示 CPU 时间或分配内存的比例。横向位置不表示函数在什么时候执行,不能把它当成请求时间线来读。

表格视图还区分 Self 和 Total。前者不包含后代函数,后者包含后代调用。一个调用入口看起来很宽,可能只是它调用的子函数成本高;判断修改位置时,应继续沿调用链向下看。

对于内存分析,更要避免把“分配多”和“保留多”混为一谈。Heap profile 记录的是窗口内的分配,无法说明这些对象随后是否被释放,也不是对象引用关系快照。一个函数不断创建短命对象,可以造成很高的分配量,却不必然持续积累存活内存。

公告还指出,采集窗口之外的分配看不到。如果大量分配发生在启动阶段,而采集时实例已运行一段时间,按需 profile 可能错过它。历史日志的时间范围和过滤条件也不会改变这个事实:采集观察的是当前窗口,不会回放日志里的旧请求。

latest 不一定是正在运行的版本

平台提供控制台、cf CLI 和 API 三种入口。官方文档允许采集时长为 1000 至 50000 毫秒,控制台默认 10000 毫秒,并支持选择 CPU 或 Heap profile。

在已安装并登录 cf、确认账号和权限后,可以参考下面的 CPU 采集示例。两个大写占位符需要替换为实际运行版本的 UUID 和 Worker 名称,本文没有执行这条命令。

1
2
3
4
cf workers versions profile YOUR_RUNNING_VERSION_UUID \
--worker-id YOUR_WORKER_NAME \
--duration-ms 5000 \
--profile-type cpu > worker-cpu.pprof

这里特意使用运行版本标识。文档说明,CLI 和 API 中的 latest 选择最新创建的版本,不保证它已经部署。如果刚上传一个版本却没有发布,照抄 latest 可能找不到可采集的运行实例。

CLI 将二进制 pprof 数据写入标准输出,可以保存后用兼容工具分析;API 返回 gzip 压缩的 pprof。采集请求也受速率限制,收到 429 后应等待,并遵循存在的 Retry-After 响应头,不要不断重试。

Durable Object 的 CLI 采集还需要指定 namespace 和实例 ID,并选择拥有该 namespace 的 Worker,不能只选一个通过 binding 调用它的 Worker。控制台中则应进入相应 namespace 和目标实例,确认它有流量并正在运行所选版本。

TypeScript 或压缩后的 JavaScript 项目,还应按官方文档准备 source map。否则 profile 中的函数名和代码位置可能难以对应原始实现。使用 Wrangler 的项目可以核对 upload_source_maps 配置;这一步需要纳入自己的构建与部署流程,本文不会修改任何实际 Cloudflare 项目。

优化之后,仍要用同类流量复核

Cloudflare 公告给出一个 R2 binding Worker 的 CPU 案例:某个 JSON replacer 已经被 JSON.stringify 逐节点调用,却又自行遍历树,形成重复工作。采样把这条路径暴露出来,代码检查才进一步确认原因。

这里值得借鉴的是证据链。先从资源分布找到候选,再看实现是否存在重复计算或不必要分配,修改后用相近的流量和采集条件复核。不能只因为某个框很宽,就推断删掉它会让整体性能按同样比例提升。

对于内存错误,还应同时看异常次数、内存指标和分配变化。减少分配可以改善压力,却不能单独证明泄漏已经解决。业务输出、错误率和延迟也需要一并验证,避免以资源下降换来功能退化。

按需采集仍可能错过偶发问题。公告说 Cloudflare 正在开发持续性能分析,但这是后续方向,不能把它当作本次已经交付的能力。

结语

Workers 的生产 profile 让函数级资源分析更容易进入日常排障。它最有价值的用法,是把版本、运行实例、真实流量和采集结果关联起来,缩小需要检查的代码范围。

记住几个边界就能避免常见误判:CPU 时间不是整个请求耗时,分配内存不是存活内存,一次实例采样不是全局汇总,当前窗口也不是历史回放。带着这些限制看火焰图,再做有针对性的验证,优化才更有依据。

参考来源

文章目录
  1. 1. 日志说明发生了什么,采样帮助定位资源花在哪里
  2. 2. 采集已有实例,不会替你启动一次请求
  3. 3. CPU 采样期间正常请求仍能运行
  4. 4. 火焰图的宽度不是时间轴
  5. 5. latest 不一定是正在运行的版本
  6. 6. 优化之后,仍要用同类流量复核
  7. 7. 结语
  8. 8. 参考来源
|