API 网关可以控制谁能调用 API、执行套餐的流量额度,并产出用量证据。但不应把网关当作定价目录、权益权威源、会计账本、支付处理器或开票系统。可靠的 API 变现架构需要明确分配这些职责,并在系统数据不一致时完成对账。
首先定义商业计量单位:出售什么、卖给谁、按什么周期结算,以及退款、重试和争议如何处理。然后再围绕这份合同设计网关策略与用量流水线。若先统计请求、再临时决定如何定价,最终数字往往既无法让工程团队解释,也无法让财务团队采信。
关键要点
- 分开管理产品套餐、权益、准入控制、计量、计价、开票与支付。
- 配额身份必须来自已认证的消费者或订阅上下文,不能来自未经验证的请求头。
- 限流计数器和响应头属于执行状态,不是财务证据。
- 用量事件应包含稳定 ID、事件时间、数量、单位、主体、产品版本和更正语义。
- 在故障发生前决定计数器或计量失败时是拒绝、放行、排队还是停售。
- 收费前要对齐网关、计量、账单与业务结果。
先定义商业合同,再设计网关策略
API 产品需要一份无歧义的报价规则:
| 决策 | 示例 | 对架构的影响 |
|---|---|---|
| 计费单位 | 已接收请求、成功交易、字节、计算秒或模型 Token | 网关未必能观察到权威结果或最终数量 |
| 计费主体 | 组织、订阅、应用、凭证或终端用户 | 决定身份映射、聚合和凭证轮换方式 |
| 周期 | 自然月、滚动 30 天或预付余额 | 通用固定窗口并不天然等于账单周期 |
| 额度 | 包含用量、突发速率、并发量或消费上限 | 准入控制与计费控制发生在不同时间 |
| 超额处理 | 拒绝、继续调用并收费、告警或要求升级 | 决定运行时失败行为 |
| 更正 | 延迟事件、重复、退款、冲正或争议 | 需要追加调整,而不是静默修改历史计数 |
除非合同明确规定,否则不要对所有响应一视同仁地计费。网关产生的 401、被限流的 429、上游 500 和已完成的支付可能需要四种不同处理。对于持久化业务结果,应用或交易系统通常才是权威来源。
为每个变现环节指定唯一责任方
1flowchart LR
2 P[产品目录与套餐版本] --> E[权益服务]
3 I[已认证消费者身份] --> E
4 E --> G[API 网关准入与配额]
5 G --> A[应用与业务结果]
6 G --> M[用量事件流水线]
7 A --> M
8 M --> R[计价与调整]
9 R --> B[账单与支付]
10 B --> C[对账与客户支持]
11 C --> P- 产品目录负责单位、价格、周期、版本和商业条款。
- 权益服务决定哪个有效套餐适用于已认证主体。
- 网关负责认证、执行已配置的访问和流量策略,并记录边界观察结果。
- 应用负责对象级授权和持久化业务结果。
- 计量流水线负责标准化并去重用量证据。
- 计价与账单系统把有效用量转换为金额、税费、抵扣、发票和支付状态。
- 对账系统比较各系统数据,并推动更正、客户支持和控制改进。
不要让网关在每个请求上同步调用计费服务。这样会把 API 可用性和延迟绑定到财务系统;若计费方已经接收记录但响应超时,还会留下结果不明的状态。
建立可信的套餐身份与计数身份
计数键必须来自已认证、受控的上下文。任何调用方都能发送的 X-Subscription-ID 只是一个字符串,不能证明调用方拥有该订阅。更安全的做法是先把经过验证的凭证或 Token 映射到 Consumer、客户端、租户或订阅记录,再把得到的内部身份放入网关策略上下文。
以下标识符应彼此分离:
- 用于轮换与事件响应的凭证 ID;
- 用于技术归属的客户端或应用 ID;
- 对应商业合同的订阅 ID;
- 用于开票的组织或账户 ID;
- 用于委托访问的终端用户主体;
- 对应一条用量事件或业务操作的幂等键。
同一个人可能使用多个应用,一份订阅也可能拥有多个轮换凭证。如果合同按订阅收费,却按凭证计数,凭证轮换后就会出现配额重置和账单碎片。
执行套餐额度,但不要把它称为计费
Apache APISIX 3.19.0 的 Consumer Group 可以在多个 Consumer 之间复用插件配置。计数身份仍取决于 limit-count 的 key,不能仅凭组成员关系就把它视为订阅级会计键。
下面是一个面向 APISIX 3.19.0 Schema 的配置片段,假设认证插件已经建立 $consumer_name。它对每个 Consumer 应用配额,并让有意使用相同 group 的路由配置共享计数状态;Redis 提供跨节点计数。这是执行示例,不是自然月套餐,也不是计费账本。
1{
2 "plugins": {
3 "limit-count": {
4 "count": 1000,
5 "time_window": 60,
6 "window_type": "sliding",
7 "key_type": "var",
8 "key": "consumer_name",
9 "group": "pro-api-minute",
10 "policy": "redis",
11 "redis_host": "redis-quota.internal.example",
12 "redis_port": 6379,
13 "redis_ssl": true,
14 "redis_ssl_verify": true,
15 "redis_server_name": "redis-quota.internal.example",
16 "allow_degradation": false,
17 "show_limit_quota_header": true,
18 "rejected_code": 429
19 }
20 }
21}只有存在经过认证的 consumer_name 时,此示例才按 Consumer 计数。在 APISIX 3.19.0 中,如果配置的变量计数键缺失,limit-count 会回退到 remote_addr;它不会失败关闭,也不会保留订阅级计数桶。必须先完成认证,并在执行此策略前拒绝缺少可信 Consumer 身份的请求,同时明确测试身份缺失路径。
如果省略 policy,limit-count 插件 默认使用本地计数器,每个 APISIX 实例分别维护自己的计数。Redis 策略可让多个实例共享计数状态,但 Redis 也会成为运行时依赖,必须单独设计其拓扑、认证、备份、延迟和失败行为。
allow_degradation 默认为 false。在 false 路径中,计数后端故障或动态变量解析出的限制无效时返回 500,而不会假装已经执行配额;true 路径则继续处理请求但不再限流。后者可以维持可用性,却也可能造成无限超额,因此它应当是一项商业与可靠性决策,而不是通用高可用开关。
show_limit_quota_header 默认为 true。true 路径会暴露剩余额度响应头;false 路径会隐藏这些响应头,但仍继续执行限流。响应头有助于客户端调整调用节奏,但并发请求、延迟同步、重试和策略变更都意味着它不适合作为发票证据。
在单 Redis 部署中,redis_ssl 和 redis_ssl_verify 都默认为 false。redis_ssl: false 表示计数器连接不受 TLS 保护;启用 TLS 但保持证书验证为 false,意味着流量已加密,却没有验证 Redis 服务端证书。示例同时启用二者,并固定 redis_server_name,从而明确 DNS/SNI 与证书身份。这会认证所配置的 Redis 端点,但不会认证 API 订阅身份。
构建可重放、可更正的用量事件
一条可靠的用量事件应当不可变且幂等,例如:
1{
2 "event_id": "usage_01K7Y8M4A9Q2",
3 "occurred_at": "2026-10-20T01:30:42Z",
4 "subject_type": "subscription",
5 "subject_id": "sub_7f3c",
6 "api_product": "maps-v2",
7 "plan_version": "pro-2026-10",
8 "meter": "accepted_requests",
9 "quantity": 1,
10 "gateway_request_id": "req_9b1d",
11 "outcome": "accepted",
12 "source": "gateway-us-east-1"
13}这是领域示例,不是 APISIX 内置事件 Schema。应使用自有 Schema 验证它:不同逻辑事件的 event_id 必须各不相同,而同一事件的每次重试必须复用同一个稳定 ID。还应分别保存事件时间和摄取时间,并通过新增调整事件来修正错误,而不是改写历史。不要包含访问 Token、机密信息或不必要的个人数据。
如果计费单位是已完成交易,应由提交该交易的系统发出或确认事件。网关请求 ID 可以关联记录,但调用方有时可以提供或重复这个值,因此它既不是财务身份,也不是通用去重键。
上线前先设计失败路径
| 故障 | 可选策略 | 必须公开并控制的内容 |
|---|---|---|
| 权益服务不可用 | 拒绝、使用有界缓存或窄范围回退 | 缓存年龄、撤销延迟、受影响套餐和事件负责人 |
| 计数器不可用 | 失败关闭或临时绕过 | 收入风险、客户影响、告警和恢复后对账 |
| 计量接收端不可用 | 持久化排队或停止接收可计费工作 | 队列容量、溢出、重放和丢失检测 |
| 重复事件 | 按稳定事件 ID 去重 | 保留窗口和碰撞处理 |
| 延迟事件 | 计入正确周期或下一张发票 | 面向客户的更正规则 |
| 计费 API 超时 | 幂等重试并查询最终状态 | 不能把超时当成拒绝 |
| 套餐变更进行中 | 为权益和生效时间设版本 | 明确定义并发请求使用哪个版本 |
执行可用性与计量持久性需要分开考虑。请求可能已被正确放行,却丢失用量记录;反过来,即使网关执行完全正确,重复事件仍可能造成重复收费。
开票前后都要对账
每个账单周期都应比较:
- 按订阅与结果分类的已认证网关请求;
- 已接收的计量事件、重复项、延迟项和隔离项;
- 适用场景中的应用确认计费结果;
- 已计价单位、抵扣和调整;
- 发票明细和支付状态。
把差异当成一等运维信号。即使差异为零,也可能是双方同时漏掉了同一区域,因此还需要来源序列缺口、合成用量和请求/事件比率等独立完整性检查。
API 变现检查清单
- 计费单位是由可观察的接收动作还是持久化结果定义的?
- 套餐、权益、网关、计量、计价、发票和支付是否各有责任方?
- 配额身份是否来自认证上下文,而不是调用方请求头?
- 凭证轮换和多个应用是否正确映射到目标订阅?
- 是否记录计数拓扑、窗口语义与同步精度?
- 是否明确
allow_degradation、额度响应头、Redis TLS 和证书验证的两条路径? - 用量事件是否不可变、幂等、有版本且不包含机密信息?
- 延迟、重复、冲正和争议用量能否在不删除历史的情况下更正?
- 故障队列是否有界且可观察?
- 对账是否覆盖网关、应用、计量、计价和发票记录?
常见问题
API 网关可以直接计算发票吗?
它可以提供流量证据并执行额度,但财务计价、税费、抵扣、发票、支付和会计控制应由专门系统负责。
429 响应是否计费?
只能由商业合同决定。大多数设计会区分已接收工作和被拒绝尝试,但防滥用、容量预留等产品可能采用不同单位。应明确编码并测试这条规则。
配额系统故障时应该放行流量吗?
没有通用答案。失败关闭可以保护额度,却可能造成中断;失败开放可以保护可用性,却带来收入和容量风险。应按产品层级决策,限制例外范围,立即告警,并在恢复后对账。
后续步骤
把商业模型与分布式 API 网关限流、API 访问日志审计和API 网关日志分析结合起来。
