电商 API 网关应避免浏览、购物车、库存、结算与合作伙伴 API 共享一个不受控制的故障域。面对流量高峰,目标不是接收所有请求,而是保住价值最高的用户旅程、防止重复状态变更,并确保降级可见且可撤销。
网关可以整形流量并控制发布,却无法创造库存、让非幂等支付自动变得安全,也无法在缺少持久应用状态时恢复已经提交的订单。
核心要点
- 为浏览、购物车、结算、库存和合作伙伴流量设置独立的容量与故障策略。
- 把结算幂等性建设成持久应用契约;网关重试策略无法凭空创造幂等性。
- 在流量高峰前为结算预留容量,再有意识地丢弃低优先级工作。
- 逐步发布路由与服务变更,但不能假设小时间窗口内的加权流量会产生精确比例。
- 将网关结果与订单和支付状态关联,同时不要把请求 ID 当作可信身份。
把商店拆分成不同工作负载
| 工作负载 | 典型特征 | 网关策略重点 | 服务责任 |
|---|---|---|---|
| 浏览与搜索 | 高流量,以读取为主,部分内容可缓存 | 缓存、宽松限流、优雅降级 | 搜索新鲜度与商品目录正确性 |
| 购物车 | 用户状态与频繁写入 | 已认证预算与有界负载 | 购物车一致性与过期 |
| 结算 | 几乎不能容忍重复或模糊结果 | 严格准入、不盲目重试 POST、稳定路由 | 幂等性、订单状态机与对账 |
| 库存 | 热点 Key 与竞争 | 请求边界与过载信号 | 预留与超卖策略 |
| 合作伙伴与 Webhook | 突发机器流量 | 合作伙伴身份、配额、签名检查 | 事件去重与业务处理 |
一个全局 RPS 限制无法覆盖这些差异。应分别保护结算并发和下游连接池,避免它们受浏览流量影响。Worker、队列、数据库和支付服务商的预留容量必须与网关预算匹配。
把结算幂等性放在网关之后
1sequenceDiagram
2 participant C as 客户端
3 participant G as API 网关
4 participant O as 订单服务
5 participant D as 持久幂等存储
6 participant P as 支付服务商
7 C->>G: POST 结算、已认证用户与幂等键
8 G->>G: 验证身份、结构、大小与准入预算
9 G->>O: 转发一次有边界的尝试
10 O->>D: 原子占用用户、操作、幂等键与请求指纹
11 alt 已完成的现有幂等键
12 D-->>O: 已存储结果
13 O-->>G: 相同业务结果
14 else 正在处理的现有幂等键
15 D-->>O: 处理中状态
16 O-->>G: 待处理或冲突响应与状态查询
17 else 新幂等键
18 O->>P: 使用服务商幂等键创建或确认支付
19 P-->>O: 服务商结果
20 O->>D: 存储持久订单与结果
21 O-->>G: 业务结果
22 end
23 G-->>C: 响应RFC 9110规定代理不得自动重试非幂等请求。只有应用已经定义并执行幂等语义,或能够证明原请求没有生效时,结算 POST 才能安全重复。
幂等键应绑定到已认证账户和规范化请求指纹。若同一个键对应不同购物车、金额或币种,应拒绝请求,而不是悄悄返回无关结果。结果的保存时间要覆盖客户端、网关、队列与支付服务商的重试窗口。
构建高峰流量准入计划
应在活动前明确保护顺序:
- 尽早拒绝格式错误、负载过大或未认证的工作;
- 按客户端和合作伙伴设置预算,避免单一来源耗尽集群;
- 依据订单、库存和支付系统容量限制结算并发;
- 先于结算功能丢弃推荐、个性化与高成本分析;
- 返回明确的过载响应,且只有在恢复时间有意义时才返回
Retry-After; - 下游队列无法在有界时间内排空时,停止接收新工作。
限流不等于容量。还应观测活跃请求、队列年龄、连接池饱和度、依赖延迟与错误类别。等队列已经失控后才触发的自动扩容,并不是完整的高峰计划。
使用受控流量切分发布版本
Apache APISIX 3.19.0 的 traffic-split 插件可以把流量分发到多个加权上游。下面的片段为 checkout v2 分配权重 1,并让路由的基础 v1 上游保留权重 9。这只是配置片段;部署时还要定义两个 Upstream 对象、健康检查、TLS 与回滚自动化。
1{
2 "uri": "/checkout",
3 "methods": ["POST"],
4 "plugins": {
5 "traffic-split": {
6 "rules": [
7 {
8 "weighted_upstreams": [
9 {"upstream_id": "checkout-v2", "weight": 1},
10 {"weight": 9}
11 ]
12 }
13 ]
14 }
15 },
16 "upstream_id": "checkout-v1"
17}APISIX 文档警告:由于使用加权轮询,实际比例可能不够精确,状态重置后尤其如此。应用足够大的样本评估金丝雀版本,并关注重复尝试、库存冲突、支付拒绝和结算完成率等业务结果,而不是只看 HTTP 错误率。
对会改变状态的流量,不要使用调用方可控请求头选择不受信任的版本。如果需要粘性分配,应从已认证、经过隐私审查的服务端 Key 派生,并验证两个版本共享兼容的订单与幂等状态。
按用户旅程定义降级行为
- 商品目录不可用:只有价格与库存语义清楚时,才能返回有边界的陈旧数据。
- 推荐服务不可用:省略推荐模块,不要拖慢结算。
- 库存缓慢:停止承诺库存,也不要接收无法按策略预留的订单。
- 支付结果不确定:显示待处理状态并执行对账;不要让客户端盲目重新提交支付。
- 订单数据库不可用:无法确认持久幂等与提交状态时,拒绝新结算。
- 遥测不可用:使用有界缓冲;不要把整个请求或支付数据写入应急日志。
熔断器可以减少对故障依赖的重复压力,但熔断打开并不代表业务成功。应为每条路由定义准确的客户端状态和恢复操作。
观测业务边界
Apache APISIX 的 request-id 插件会保留指定请求头中非空的调用方请求 ID;请求头缺失或值为空时才生成新值。因此,该值适合关联,却不是可信身份。若要保留外部 ID,应验证其长度与格式,并与已认证主体分开记录。
记录网关路由、发布版本、准入结果、上游尝试、延迟和响应类别,再与应用拥有的订单、库存和支付引用关联。网关返回 200 不能证明支付已结算,超时也不能证明支付失败。
高峰准备检查清单
- 浏览、购物车、结算、库存、合作伙伴与 Webhook 预算是否彼此独立?
- 结算是否执行持久幂等键与请求指纹?
- 每条非幂等重试路径是否都能解释且有明确边界?
- 压力测试是否覆盖热门商品、缓慢支付和队列饱和?
- 低优先级工作是否会在结算容量前被丢弃?
- 停止金丝雀版本时是否会丢失幂等或订单状态?
- 依赖故障是否会呈现准确的待处理、重试或不可用状态?
- 网关证据能否与持久订单和支付记录对账?
常见问题
结算 POST 超时后,网关是否应该重试?
不能盲目重试。只有应用契约已经让操作具备幂等性,并且重试复用同一个幂等键时才可重试;否则应查询或对账现有操作。
浏览与结算是否应该共享一个限流策略?
不应该。它们的价值、成本和故障后果不同,应使用独立的路由、Consumer、并发与依赖预算。
90/10 流量切分是否会精确产生 10% 金丝雀流量?
不会。权重只是分配机制,小样本或状态重置可能产生偏差。应测量实际分布和业务结果。
后续步骤
添加并发预算与背压,定义安全的超时与重试策略,并使用网关 CI/CD 验证与回滚管理变更。
