Skip to content

Edge Function shutdown reasons explained

了解 Edge 函数(worker)可能关闭的不同原因,以及如何有效应对每种情况。

🌐 Learn about the different reasons why an Edge Function (worker) might shut down and how to handle each scenario effectively.

了解关机事件 #

🌐 Understanding shutdown events

当一个 Edge Function 停止执行时,运行时会发出一个带有特定 reasonShutdownEvent,用来解释工作进程结束的原因。理解这些关闭原因可以帮助你诊断问题、优化函数,并构建更有弹性的无服务器应用。

🌐 When an Edge Function stops executing, the runtime emits a ShutdownEvent with a specific reason that explains why the worker ended. Understanding these shutdown reasons helps you diagnose issues, optimize your functions, and build more resilient serverless applications.

这些事件会通过日志和可观测性工具显示出来,让你可以监控函数的行为,并采取适当的措施,比如重试操作、调整资源限制或优化代码。

🌐 These events are surfaced through logs and observability tools, allowing you to monitor function behavior and take appropriate action such as implementing retries, adjusting resource limits, or optimizing your code.

关闭原因 #

🌐 Shutdown reasons

EventLoopCompleted#

意思是: 函数的事件循环自然结束了。没有更多的待处理任务、定时器或微任务。工作线程成功完成了所有已安排的工作。

发生时: 这是正常的、优雅的关闭场景。所有同步代码都已执行完,所有等待的 promise 也都已解决。

要做的事情: 没什么特别的。这表示已成功完成。无需重试。

WallClockTime#

意思是: 工作者超出了配置的实际时间限制。这指的是从函数开始计算的总经过时间,包括等待 I/O、外部 API 调用以及休眠的时间。

**什么时候会发生:**你的函数运行时间太长,无论它实际在执行多少计算。目前,墙钟时间的限制设为 400 秒。

该做什么:

  • 把长时间运行的任务拆分成更小的函数
  • 对需要时间的操作使用流式响应
  • 考虑把长时间运行的工作移到后台任务或队列
  • 确保你的代码能优雅地处理部分完成情况
  • 让操作具备幂等性,这样就可以从头安全地重试

相关: 更多详情请参见 Edge Function '墙上时钟时间限制已到'

CPU时间 #

🌐 CPUTime

意思是: 这个任务消耗的 CPU 时间超过了允许的范围。CPU 时间指的是你的代码实际使用的处理周期,不包括等待 I/O 或休眠的时间。目前限制为 2000 毫秒。

发生这种情况时: 你的功能正在执行过多的计算。这包括复杂的计算、数据处理、加密或其他 CPU 密集的操作。

该做什么:

  • 分析你的代码,找出占用 CPU 的部分
  • 优化算法以提升性能
  • 考虑缓存计算结果
  • 把繁重的处理任务移到后台作业或外部服务
  • 把大数据集分成小块
  • 使用更高效的数据结构

记忆 #

🌐 Memory

意思是: 工作者的内存使用超过了允许的限制。ShutdownEvent 包含详细的内存数据,显示总内存、堆使用情况以及外部分配。

发生这种情况时: 你的函数占用了太多内存。这通常发生在缓冲大文件、将整个数据集加载到内存中,或者创建了很多对象却没有清理时。

该做什么:

  • 使用流式传输,而不是缓冲整个文件或响应
  • 分块处理数据,而不是一次性加载所有内容
  • 注意那些可能阻止垃圾回收的闭包和变量
  • 尽量不要堆积大量数组或对象
  • 查看日志中的 memory_used 字段,确认是堆内存还是外部内存出了问题

EarlyDrop#

意思是: 运行时检测到函数已经完成了所有工作,可以提前关闭,而不必达到任何资源限制。这是最常见的关闭原因,通常意味着函数执行效率很高。

何时发生: 你的函数已经完成了请求的处理,发送了响应,并且没有剩余的异步工作(未决的 Promise、定时器或回调)。运行时会识别到可以安全地终止这个工作线程,而无需等待超时或其他限制。

为什么这是好的: EarlyDrop 意味着你的函数运行得很高效。它完成时没有耗尽资源,并且运行时可以将工作线程回收用于其他请求。大多数设计良好的函数应该以 EarlyDrop 结尾。

该做什么:

  • 无:这是大多数函数期望的结果
  • 如果你看到 EarlyDrop 但本来预期会有更多操作发生,检查一下:
    • 没有被正确等待的承诺
    • 没有清理的事件监听器或定时器
    • 本应该完成但没完成的后台任务
  • 如果你故意有一些后台任务需要在返回响应后继续执行,确保在函数返回之前这些承诺都已经被等待

TerminationRequested#

意思是: 有外部请求明确要求运行时终止工作进程。这可能来自编排系统、人工干预、平台更新、部署,或用户主动取消。

发生时: 平台需要立即停止你的功能,通常是在部署或基础设施维护期间。

该做什么:

  • 实现优雅的清理代码,但要预料到它可能不会总是运行
  • 为突然终止设计。使用耐用存储来保存正在进行的工作
  • 尽可能让操作成为原子操作或使用事务
  • 在外部跟踪执行状态,这样可以检测到未完成的工作并继续进行
  • 测试你的函数在被强制停止时的表现

额外诊断事件 #

🌐 Additional diagnostic events

虽然这些本身不是停工原因,但这些事件提供了重要的背景信息:

🌐 While not shutdown reasons themselves, these events provide important context:

  • 启动事件 / 启动失败: 表示一个工作节点在初始化时是否成功启动或失败
  • 未捕获异常: 表示出现未处理的错误,包含异常信息和 CPU 使用时间
  • LogEvent: 运行时生成的日志,带有不同严重级别(调试、信息、警告、错误)
  • WorkerMemoryUsed: 包含总内存、堆内存、外部内存和内存检查器数据的详细内存快照

关机事件元数据 #

🌐 Shutdown event metadata

每个 ShutdownEvent 都包含有价值的诊断信息:

🌐 Each ShutdownEvent includes valuable diagnostic information:

  • reason:上面描述的关机原因之一
  • cpu_time_used:消耗的 CPU 时间(毫秒)
  • memory_used:关机时的内存快照,包括总内存、堆内存和外部内存的细分
  • execution_id:用于在日志和重试中跟踪此特定执行的唯一标识符

打造高韧性功能的最佳实践 #

🌐 Best practices for resilient functions

1. 为幂等性设计 #

🌐 1. Design for idempotency

让你的函数可以安全地多次用相同输入运行。利用元数据中的执行 ID 来检测重复运行,避免重复的副作用。

🌐 Make your functions safe to run multiple times with the same input. Use execution IDs from metadata to detect duplicate runs and avoid repeating side effects.

1
// Store execution_id to detect retries
2
const executionId = Deno.env.get('EXECUTION_ID')
3
const alreadyProcessed = await checkIfProcessed(executionId)
4
5
if (alreadyProcessed) {
6
return new Response('Already processed', { status: 200 })
7
}

2. 实现检查点功能。 #

🌐 2. Implement checkpointing.

经常把进度保存到耐用存储中

🌐 Save progress frequently to durable storage

1
// Save progress incrementally
2
for (const batch of dataBatches) {
3
await processBatch(batch)
4
await saveProgress(batchId)
5
}

3. 对大数据使用流式传输 #

🌐 3. Use streaming for large data

避免把整个文件或响应加载到内存中。通过流式传输数据来减少内存占用。

🌐 Avoid loading entire files or responses into memory. Stream data to reduce memory footprint.

1
// Stream responses instead of buffering
2
return new Response(readableStream, {
3
headers: { 'Content-Type': 'application/json' },
4
})

4. 监控和警报 #

🌐 4. Monitor and alert

在你的可观测系统中跟踪关闭原因。设置以下警报:

🌐 Track shutdown reasons in your observability system. Set up alerts for:

  • 经常 Memory 关闭:调查内存使用模式
  • 频繁的 CPUTime 关机:优化计算工作
  • 经常性的 WallClockTime 关机:减少延迟或分拆工作
  • 频繁出现 EarlyDropTerminationRequested:检查平台的扩展和部署模式

函数日志查看你的函数日志。

🌐 Access your function logs at Functions Logs.

5. 优雅地处理清理工作 #

🌐 5. Handle cleanup gracefully

虽然你不能总是依赖清理代码运行,但还是要实现它,以应对可以优雅关闭的情况。

🌐 While you can't always rely on cleanup code running, implement it anyway for the cases where graceful shutdown is possible.

1
// Cleanup handler (may not always run)
2
addEventListener('unload', () => {
3
// Close connections, flush buffers, etc.
4
cleanup()
5
})

示例关机事件负载 #

🌐 Example shutdown event payloads

正常完成:

1
{
2
"event": {
3
"Shutdown": {
4
"reason": "EventLoopCompleted",
5
"cpu_time_used": 12,
6
"memory_used": {
7
"total": 1048576,
8
"heap": 512000,
9
"external": 1000
10
}
11
}
12
},
13
"metadata": {
14
"execution_id": "4b6a4e2e-7c4d-4f8b-9e1a-2d3c4e5f6a7b"
15
}
16
}

挂钟超时:

1
{
2
"event": {
3
"Shutdown": {
4
"reason": "WallClockTime",
5
"cpu_time_used": 50,
6
"memory_used": {
7
"total": 2097152,
8
"heap": 1024000,
9
"external": 5000
10
}
11
}
12
},
13
"metadata": {
14
"execution_id": "5c7b5f3f-8d5e-5g9c-0f2b-3e4d5f6g7h8i"
15
}
16
}

故障排查清单 #

🌐 Troubleshooting checklist

在调查关机问题时,请使用这个快速参考:

🌐 Use this quick reference when investigating shutdown issues:

关闭原因主要操作
多次 Memory 关闭切换到流式处理;分块处理数据;检查堆内存和外部分配
多次 CPUTime 关闭优化算法;缓存结果;将高负载计算移到后台工作线程
多次 WallClockTime 关闭减少 I/O 等待;使用异步操作;拆分成更小的函数
经常出现 EarlyDropTerminationRequested检查平台扩展策略;查看部署日志;实现更好的检查点机制

额外资源 #

🌐 Additional resources