华为云 FunctionGraph 函数超时/失败分层排查:从 Handler、超时与内存到冷启动、并发与 VPC
华为云 FunctionGraph 函数超时/失败分层排查:从 Handler、超时与内存到冷启动、并发与 VPC
FunctionGraph 把计算做成按需拉起的函数实例,省掉了常驻服务器的运维,但调用失败或偶发变慢时,原因往往分散在执行入口、超时配置、内存规格、依赖包、冷启动、并发上限和网络几层。很多人一上来就调大超时或内存,其实先看「监控 > 日志」里的原因分析,再按现象分层核对,会快很多。本文对照华为云官方文档,整理一份可复用的排查清单。
一、先看日志:把失败类型定下来
函数详情页进入「监控 > 日志」。请求列表可按调用结果、原因分析筛选。官方把执行失败细分为:
| 原因分析 | 含义(官方说明) |
|---|---|
| 初始化失败 | 初始化阶段失败 |
| 加载失败 | Runtime 加载用户函数文件失败 |
| 系统错误 | 内部错误 |
| 调用超时 | 执行时间超过配置的「执行超时时间」 |
| 内存超限 | 实际内存超过配置的「内存」大小 |
| 磁盘超限 | 临时磁盘超出限制 |
| 代码异常 | 业务代码抛错 |
测试页签里的执行结果最多显示约 2KB 日志;日志太多时去「监控 > 日志」或跳到 LTS 看全量。请求列表还能看到是否经过冷启动,以及耗时、实际使用内存——后面几层排查都依赖这些字段。
日志上报有延迟,刚测完若列表为空,等一两分钟再刷。若误删了 LTS 里函数对应的日志组,旧日志找不回;官方建议改一下常规设置里的描述并保存,可触发重建日志组。
二、加载失败与执行入口:代码根本没跑起来
加载失败、或提示找不到 Handler,优先核对这几项。
1. 执行入口格式
在「设置 > 常规设置」看「函数执行入口」,规则因运行时不同:
- Node.js / Python / PHP:
[文件名].[执行函数名],必须带点,默认常为index.handler - Java:
[包名].[类名].[执行函数名] - Go:与可执行文件名一致
- C#:
[程序集名]::[命名空间].[类名]::[执行函数名]
入口名与代码里真实导出的函数名不一致,是很常见的配置笔误。改完保存后再测一次。
2. ZIP 包目录结构
官方反复强调:制作 ZIP 时,单函数入口文件必须在根目录,解压后直接看到入口文件,而不是多套一层文件夹。很多人本地打包时把整个项目目录再套一层,上传后 Runtime 找不到入口文件,就会落在「加载失败」。
Python 还有一条:自建包名不要和标准库撞名(如 json、os、lib),否则可能 module 加载失败。
3. 代码包与依赖包大小
对照官方约束(以当前文档为准,区域/版本可能微调):
- 控制台前端上传 ZIP:约 40MB;接口上传 ZIP:约 50MB;更大请走 OBS(压缩包最大约 300MB)
- 私有依赖包直接上传 ZIP:约 10MB;更大同样走 OBS
- 依赖包内文件名不能以
~结尾;文件数有上限(文档写 30000) - 临时磁盘
/tmp:512MB
依赖包过大时,冷启动下载和解压都会变慢;官方建议私有依赖很大时,在原超时基础上适当加长执行超时,避免还没跑完业务就被掐掉。
三、调用超时:三层时长别搞混
官方在常规设置里写得很清楚,超时其实有三层:
- 执行超时时间(用户可配):范围 3~259200 秒,默认 3 秒。超过该值函数会被强行停止,日志原因分析为「调用超时」。
- 系统最大运行时长:同步调用最长 15 分钟(900 秒);异步调用最长 259200 秒(3 天 / 72 小时)。用户配置的执行超时必须落在系统上限之内。
- 控制台前端超时:页面等待后端约 90 秒。若执行超时配成大于 90 秒,且实际跑得也超过 90 秒,前端会提示超时,但后端可能仍在跑,结果要去日志里看。
另外:若预计执行时间超过 900 秒,官方建议改用异步调用。同步调用请求/响应正文有效负载约 6MB;异步调用请求/响应约 256KB,异步返回值超过 256KB 可能得到空值。
排查步骤建议:
- 在日志里确认是不是「调用超时」,并看实际耗时。
- 对照业务:初始化拉模型、连库、调下游 API,是否经常超过当前超时。
- 把可复用的重逻辑挪到 Initializer(初始化入口):实例生命周期内初始化只成功执行一次,适合加载大模型、建连接池;初始化超时可配 1~300 秒,且初始化入口须与执行入口在同一文件。
- 长任务改异步,并在「设置 > 异步配置」里配失败重试与结果通知(OBS / SMN / DIS / 下游函数等)。注意不要配成 A 成功通知 B、B 成功又通知 A 的循环。
四、内存超限:runtime memory limit exceeded
实际占用超过配置内存时,常见返回 runtime memory limit exceeded,原因分析为「内存超限」。官方说明:到函数请求返回界面查看实际内存占用。
内存规格可选:128、256、512、768、1024……直至 8192、10240(单位 MB),默认 128。内存不只是「装得下」,在并发一定时,更大内存通常对应更多 CPU,冷启动与执行往往也会更快一些(见冷启动最佳实践)。
处理顺序:
- 看返回界面或日志里的实际使用内存,确认是偶发尖峰还是稳定打满。
- 先精简依赖与数据结构,避免在 Handler 里反复加载大文件到内存。
- 再按阶梯上调内存规格并复测。
- 大对象加载尽量放进 Initializer,让热实例复用。
五、冷启动:第一次慢不一定是代码写坏了
冷启动是 Serverless 的常态:当前没有可复用的激活实例时,平台要下载代码、创建执行环境、跑初始化,再处理请求。日志里可以看到某次请求是否「冷启动成功」。间隔一段时间无流量后实例会被回收,下一次又会冷启动。
官方用户侧优化建议可以概括为:
| 手段 | 要点 |
|---|---|
| 选合适内存 | 内存越大,分配的 CPU 通常越多,冷启动往往更好 |
| 精简代码/镜像 | 去掉无用依赖、测试文件;镜像函数镜像越大启动越慢 |
| 公共依赖包 | 平台侧预缓存,优先于体积很大的私有依赖包 |
| 预热 | 定时触发器定期打一下;或配置预留实例避免冷启动 |
| Java 快照冷启动 | Java 可开快照式冷启动,从初始化后的快照恢复环境 |
注意:预留实例存在时,更新函数代码和配置可能被限制(错误码侧有「存在预留实例不能更新」一类提示),改配置前先看预留实例状态。
六、并发打满:FSS.0429 与最大实例数
默认每个实例同一时刻处理一个请求;也可以开「单实例多并发」(主要面向空白/模板创建的 HTTP 函数,以及容器镜像函数等,以当前控制台为准)。
关键参数:
- 单实例并发数:1~1000,默认 1
- 单函数最大实例数:1~1000 或 -1(不限制),默认 400
- 实例数 ≈ 函数并发数 / 单实例并发数
超过实例处理能力时,请求可能被流控:Body 中 ErrorCode 为 FSS.0429,HTTP Status 429,提示类似 Your request has been controlled by overload sdk, please retry later。官方还说明:超过最大实例数处理能力的请求会被直接丢弃而不是重试;这类错误不一定直接出现在函数业务日志里,异步场景可通过异步调用策略拿更完整的错误信息。
单实例多并发时还有两个坑:
- 日志不要再用会拼错 Request ID 的
print/console.info,改用context.getLogger() - 共享变量要做互斥,非线程安全的全局状态容易在并发下写乱
Python(GIL)和 Node.js(单线程)即便配了更大规格,多并发也未必吃满多核,这点官方文档写得很直白。
七、网络:开了 VPC 就别再指望默认公网
函数创建后默认可用共享公网出网(适合测试,生产不保证带宽可靠性)。一旦开启「函数访问 VPC 内资源」:
- 会禁用默认网卡,改用 VPC 网卡
- 「允许函数访问公网」开关不再生效
- 要出公网,需要在该 VPC 配公网 NAT 网关 + EIP(SNAT)
委托权限:访问 VPC 需要给函数委托配置 VPC 相关权限(文档示例为 VPC Administrator,或按最小权限授权 ports/vpcs/subnets 的增删查);VPC 内有内网域名时还要 DNS 只读类权限。
安全组:官方要求在 default 安全组为函数所选 VPC 网段放通 ICMP 入方向,并按业务放开出方向端口——访问 VPC 内 RDS/DCS 等不通时,先查安全组与子网是否 ACTIVE(子网非 ACTIVE 会碰到 FSS.1090 一类错误)。
账号级还有「所有函数最多绑定 4 个不同子网」的限制,绑多了要合并或调整。
八、常见坑速览
- 默认超时只有 3 秒:Hello World 够用,调一次下游 HTTP 就很容易超时。
- 前端 90 秒超时 ≠ 函数已失败:长任务以后端日志和异步结果为准。
- ZIP 多套一层目录:加载失败的第一嫌疑。
- 开 VPC 后外网全挂:不是「偶发网络抖动」,是默认公网被关掉了。
- 只调大内存不看依赖:大私有依赖既拖冷启动,又更容易超时。
- 并发打满只看业务日志:429 / 丢弃类错误可能不在业务 print 里,要看调用侧响应和异步通知。
- Initializer 里挂载盘读写:官方明确不建议,可能导致读不到磁盘内容。
- 异步通知配成环:A→B→A 会循环调用。
九、分层排查顺序(建议照着走)
- 「监控 > 日志」定原因:超时 / 内存 / 加载 / 代码异常 / 初始化失败。
- 加载类:核对执行入口、ZIP 根目录、依赖是否损坏或超限。
- 超时类:看实际耗时 → 调执行超时或改异步 → 重逻辑进 Initializer。
- 内存类:看返回界面实际占用 → 精简或升内存规格。
- 偶发首包慢:按冷启动手段优化(公共依赖、预留实例、定时预热)。
- 高并发失败:看 429 / FSS.0429,调最大实例数或单实例并发;评估是否丢弃。
- 访问 VPC/公网异常:核对 VPC 开关、NAT、安全组、委托权限。
十、速查表
| 现象 | 优先核对 |
|---|---|
| 加载失败 / 找不到 Handler | 执行入口格式、ZIP 是否多套目录、Python 包名冲突 |
| 调用超时 | 执行超时是否仍为默认 3 秒;是否应改异步;依赖是否过大 |
| 前端提示超时但日志成功 | 是否超过前端约 90 秒等待 |
| runtime memory limit exceeded | 返回界面实际内存;上调规格或减负 |
| 第一次慢、后面快 | 冷启动;公共依赖包 / 预留实例 / Initializer |
| HTTP 429、FSS.0429 | 最大实例数、单实例并发;是否丢弃请求 |
| 开 VPC 后访问不了公网 API | NAT+EIP;默认公网已禁用 |
| 访问不到 VPC 内数据库 | 安全组、子网 ACTIVE、委托权限 |
附:一份最小可核对的配置清单(Python 示例)
下面不是「必须照抄的业务代码」,只是方便对照控制台字段。假设入口文件是根目录的 index.py:
# index.py
def handler(event, context):
logger = context.getLogger()
logger.info("request_id=%s" % context.getRequestID())
return {"statusCode": 200, "body": "ok"}
def my_initializer(context):
# 适合:加载只读配置、建连接池、加载模型权重
# 不适合:依赖临时挂载盘读写的逻辑
pass
控制台侧建议逐项核对:
- 函数执行入口:填
index.handler(与文件名、函数名一致)。 - 初始化配置(若开启):入口填
index.my_initializer,与执行入口同文件;初始化超时按加载耗时留余量(可配 1~300 秒)。 - 执行超时时间:先按 P99 耗时的 2~3 倍估算,不要长期停在默认 3 秒;超过 900 秒的同步调用应改为异步。
- 内存:看几次真实调用的实际占用后再选档,避免默认 128MB 在引入重型库后直接 OOM。
- 依赖包:能用公共依赖包就不用大私有包;私有包 ZIP 直接上传约 10MB 封顶,更大走 OBS。
- 网络:只有在需要访问 VPC 内 RDS/DCS/内网 API 时才开「函数访问 VPC 内资源」;开了就同步规划 NAT 出网,而不是事后才发现公网调用全失败。
- 并发:压测时同时盯调用侧是否出现 429 /
FSS.0429,以及最大实例数是否把请求直接丢掉。
ZIP 打包时在项目根执行(入口文件在压缩包根目录,而不是 project/index.py 多套一层):
zip -r function.zip index.py
# 若有本地私有依赖目录,再按官方依赖包规范单独制作依赖 ZIP 并在控制台添加
十一、小结
FunctionGraph 的失败信息其实已经按「原因分析」分好了类;排查时先定类型,再动配置,比盲目加大超时和内存更有效。超时要分清用户执行超时、系统同步/异步上限和控制台前端等待;内存与冷启动往往一起优化;并发和 VPC 则是流量打上来或接入内网资源后才集中暴露的问题。把日志、常规设置、并发和网络四块控制台页面跑通一遍,大多数调用失败都能落到具体动作上。
以上参数与上限以华为云当前官方文档为准,控制台若有区域差异或版本更新,以控制台与文档最新说明为准。
- 点赞
- 收藏
- 关注作者
评论(0)