跳转到正文
API 网关指南
本文目录

第 99 篇

运营 Serverless API:冷启动、幂等性与异步交接

API 网关可以认证和限制 Serverless 请求,把它路由到函数或提交服务,并向客户端提供稳定契约。但网关无法消除冷启动,无法让已经超时的函数调用自动变得可安全重放,也不能把队列接受误写成业务工作已经完成。

同步与异步 API 应设计成两种不同的契约。同步调用的截止时间必须覆盖网关处理和函数启动;异步调用则需要持久的操作身份、状态模型、重复处理、保留策略和终态失败路径。

核心要点

  • 只有当完整延迟分布都能放进客户端截止时间时,才选择同步执行。
  • 如果函数可能已经开始执行,网关超时就应被视为结果未知。
  • 幂等性必须落实在应用状态转换中,而不能只依赖网关请求头。
  • 只有持久交接完成后才返回 202 Accepted,并提供操作状态契约。
  • 分开描述网关准入、云队列接受、函数执行和业务完成。
  • 测试冷、热、限流、重复、过期和重放请求。

先选择交互契约

契约客户端收到什么适用场景必须设计的主要故障
同步最终操作响应工作能够稳定地在总截止时间内完成执行开始后超时
异步提交操作 ID 和状态位置工作耗时长、突发明显或适合后台重试已接受但稍后失败或过期
事件摄取事件进入平台的确认生产方不需要与请求关联的结果重复投递与乱序
回调或 Webhook稍后把结果推送给消费者消费者能够认证回调并处理重试伪造、重复或无法送达的回调

不要只在长时间同步函数前增加网关超时。这样会同时占用客户端、网关和函数容量,还会让恢复结果更不明确。

分开各个参与者

1flowchart LR
2    C[经过认证的客户端] --> G[API 网关]
3    G -->|短同步请求| F[函数端点]
4    G -->|经过校验的提交| S[提交服务]
5    S -->|持久入队| Q[托管队列或事件服务]
6    Q --> W[函数 Worker]
7    W --> D[幂等状态转换]
8    D --> O[操作状态存储]
9    C -->|轮询操作 ID| G
10    G --> O
11    F -. 执行结果 .-> G
12    Q -. 接受与投递证据 .-> M[运维遥测]
13    W -. 执行与终态结果 .-> M

网关负责准入和路由;提交服务负责持久入队和创建操作;队列或云服务决定投递;函数负责执行;应用状态存储决定重复请求是否改变状态。正文、指标和事故响应都应保持这些责任边界。

明确预算冷启动

冷调用可能先经历执行环境分配、运行时初始化、依赖加载、网络设置和应用初始化,然后才进入 Handler。应在接近生产的包大小、内存、网络、区域和依赖条件下测量每个函数版本。

同步路由的预算应满足:

1客户端截止时间 > 网关处理 + 建连 + 平台排队/启动 + 函数执行 + 响应传输 + 恢复余量

预置并发可以在 AWS Lambda 收到调用前准备执行环境,但会增加成本,需求超过预置水平时也可能溢出。调用方必须指向拥有预置容量的版本或别名;调用其他版本不会使用该容量池。应把它当作经过度量的延迟与成本选择,而不是“所有请求都热启动”的承诺。

示例:代理有界的函数端点

Apache APISIX 3.19.0 固定版本的 aws-lambda 插件源码通过共享的 generic-upstream 实现调用配置的函数 URL 或 API 端点。下面是一个可运行形态的 Route 请求体;请替换占位 URI,并通过批准的机密路径配置云认证。

1{
2  "uri": "/reports/preview",
3  "plugins": {
4    "aws-lambda": {
5      "function_uri": "https://example-id.lambda-url.us-east-1.on.aws/preview",
6      "timeout": 2500,
7      "max_req_body_size": 1048576,
8      "ssl_verify": true,
9      "keepalive": true,
10      "keepalive_timeout": 60000,
11      "keepalive_pool": 20
12    }
13  }
14}

插件会在 max_req_body_size 范围内缓冲请求体;读取请求体失败(包括请求体过大)时返回 HTTP 400。上游请求本身失败时,通用实现返回 HTTP 503。函数返回 400 或更高状态码时,此插件路径会把响应透传,而不会自动重试。

相关行为布尔参数如下:

参数默认值true 路径false 路径与信任边界
ssl_verifytrue验证 function_uri 的 TLS 服务端证书关闭服务端证书验证;仅有加密不再能证明对端就是预期端点
keepalivetrue复用符合条件的上游连接,并应用连接池和超时设置不复用连接,连接池设置也不生效

插件源码还会保留调用方提供的 Authorization 或 x-api-key,而不会用配置的云认证覆盖它。在进入此插件前,应删除或拒绝不可信的客户端云凭证请求头;如果确实要透传,则必须证明这是预期契约。不要让调用方选择 function_uri、区域或云服务。

从 APISIX 视角看,这条代理路径仍然是同步 HTTP。它本身不会创建持久异步操作。

设计持久的异步交接

安全的提交流程如下:

  1. 认证调用方,并映射到服务端策略;
  2. 校验负载大小、Schema 和操作类型;
  3. 要求或生成稳定幂等键;
  4. 创建带唯一 ID 的操作记录;
  5. 持久入队,并提交已接受状态;
  6. 返回带操作 ID 和状态 URL 的 202 Accepted;
  7. 以幂等方式处理消息;
  8. 写入终态成功或失败;
  9. 按明确的保留策略清理负载和状态数据。

如果创建操作和发布队列消息无法原子完成,应使用事务性 Outbox 等恢复设计。只启动内存任务后不能返回 202。

响应示例:

1HTTP/1.1 202 Accepted
2Content-Type: application/json
3Location: /operations/op_01JEXAMPLE
4Retry-After: 3
5
6{"operation_id":"op_01JEXAMPLE","status":"accepted"}

accepted 表示平台已经在约定的保留和重试契约下持久接受责任,并不表示函数已经运行或业务操作已经成功。

把幂等性做成应用不变量

只有当应用把幂等键绑定到以下信息时,它才有意义:

  • 经过认证的调用方或租户;
  • 操作类型和规范化请求指纹;
  • 已存储结果或终态;
  • 保留窗口;
  • 同一键携带不同负载时的冲突行为。

网关可以校验幂等键是否存在和格式是否正确,但改变状态的服务必须把该键与业务转换原子绑定。调用方提供的键只是关联值,不是可信身份;必须用认证上下文为它划分命名空间。

重复请求的指纹相同时,应返回原操作或原结果;同一键用于不同工作时则应返回冲突。除了顺序重试,还要测试并发重复。

理解云端重试语义

对于 AWS Lambda Event Function,异步调用会把事件放入队列,并在 Lambda 接受后返回 202。AWS 文档说明,函数错误默认再重试两次;限流和系统错误默认可能在最长六小时内重试。由于队列最终一致,即使函数没有报错也可能重复投递。

这些是 Lambda 服务语义,不是 APISIX 插件的执行模式。应为所选函数配置最大事件年龄、重试次数和失败目标或死信路径。即使减少重试,Handler 仍须保持幂等。

处理同步请求的未知结果

网关超时时,应判断函数是否可能仍在运行或已经提交副作用。如果答案是肯定的,结果就是不明确。没有幂等不变量和状态查询时,不要自动重放支付、消息或资源创建请求。

如果工作流可能比连接存续更久,应尽早返回操作 ID。对于真正同步的操作,要把总重试预算限制在客户端截止时间内,并且只在 HTTP 方法和应用语义允许时重试。

观察每一个阶段

分别跟踪:

  • 网关准入、拒绝、超时和上游状态;
  • 提交接受与入队延迟;
  • 队列年龄、积压、重复投递和过期;
  • 冷/热初始化与函数执行时间;
  • 并发限流和云厂商错误;
  • 幂等命中、冲突和重复抑制;
  • 业务成功、失败和不明确状态;
  • 回调尝试和最终送达结果。

使用 API、操作类型、函数别名、环境和终态等有界标签。不要把请求 ID、用户 ID 或幂等键放入 Prometheus 标签;在策略允许时把它们放入受访问控制的日志或 Trace。

Serverless API 检查清单

  • API 是否明确属于同步、异步、事件或回调模式?
  • 同步总截止时间是否包含冷启动和恢复余量?
  • 副作用开始后是否可能超时,如何解决结果不明确问题?
  • 返回 202 Accepted 前是否完成了持久交接?
  • 幂等键是否按认证身份和请求指纹划分命名空间?
  • 是否配置并测试队列重试、重复投递、过期和终态失败?
  • 是否记录 ssl_verify 与 keepalive 的默认值和两条路径?
  • 是否删除不可信的客户端云认证请求头,或明确允许其透传?
  • 运维人员能否关联网关、队列、函数和业务结果?
  • 回滚是否保留兼容的函数别名、事件 Schema、凭证和状态记录?

常见问题

API 网关能消除 Lambda 冷启动吗?

不能。它可以复用上游连接并路由到已准备的别名,但执行环境就绪状态属于 Serverless 平台和函数配置。

202 Accepted 表示函数已经成功吗?

不表示。它只表示选定的提交边界接受了工作。还需要状态契约和终态失败路径。

只增加幂等请求头就能防止重复副作用吗?

不能。应用必须把幂等键与认证上下文、请求含义和业务状态转换原子绑定。

后续步骤

应用安全的超时与重试策略,设计异步网关模式,并通过网关变更管理交付变更。

编辑本篇文章

获取方案