PAYATHON 2026

如何按 API key 获取 Google Cloud Endpoints API 用量

支付阿杰

结论

建议将 API key 与客户身份绑定,由 ESP/ESPv2 负责认证和请求上报,再把 Cloud Endpoints 的请求日志持续导出到 BigQuery,按”客户(或 consumer project)+ API 方法 + 月份”汇总调用次数。

Cloud Monitoring 图表和原始 API key 都不适合直接充当计费账本:

  • Monitoring 适合观察趋势和设置告警,不适合长期保存可审计的逐请求计费明细。
  • 原始 API key 是凭据,不应写入日志或数据库。
  • 为每个客户分配一个 GCP consumer project 是可行方案,但并非必需。这样做主要是为了隔离配额、权限和消费归属。
  • 正式计费前,还要明确哪些请求属于有效调用,例如是否排除认证失败、限流、5xx、健康检查和重试请求。

推荐的数据链路

客户端
  │ API key
  ▼
ESP / ESPv2
  │ 验证凭据并识别 API operation
  ▼
Cloud Logging
  │ Log Router sink
  ▼
BigQuery
  │ 按月份、客户、API method 聚合
  ▼
月度账单

统计时应使用稳定且不敏感的客户标识,例如:

  • consumer project number
  • API key 的内部 ID
  • 自己维护的 client_id
  • API key 的不可逆哈希值

不要保存或输出 12345、67890 这类原始 key。

是否需要每个客户一个 GCP project

如果客户可以拥有自己的 Google Cloud project,或者需要为不同客户分别管理配额,可以采用以下映射关系:

client_a -> consumer-project-a -> API key A
client_b -> consumer-project-b -> API key B

然后要求客户在各自的 consumer project 中启用你的 Endpoints 服务。Service Infrastructure 上报的请求通常可以关联到消费方 project,因此可以按 consumer project 汇总日志或监控数据。

这种设计有几个好处:

  • 可以为不同客户设置独立配额。
  • API key 泄露时,影响范围较小。
  • 客户身份不依赖可能变化的 key 字符串。
  • 客户轮换 API key 后,仍然归属于同一个 consumer project。
  • 同一客户拥有多个 key 时更容易管理。

如果所有 key 都由服务提供方统一创建和管理,也可以维护一张映射表:

credential_id -> client_id

这样就不必为了统计而创建大量 project。Cloud Endpoints 实际记录的凭据字段和日志结构会随 ESP/ESPv2、部署方式和日志类型而变化,因此要先检查生成的结构化日志,再确定 BigQuery 查询使用哪些字段。

实施步骤

1. 为每个 API operation 设置稳定名称

在 OpenAPI 配置中,为 operation 设置稳定且唯一的 operationId:

paths:
  /esp1:
    get:
      operationId: api_esp1
      security:
        - api_key: []

  /esp2/foo:
    post:
      operationId: api_esp2_foo
      security:
        - api_key: []

  /esp2/bar:
    post:
      operationId: api_esp2_bar
      security:
        - api_key: []

securityDefinitions:
  api_key:
    type: apiKey
    name: key
    in: query

条件允许时,优先通过请求头传递 key,避免它出现在 URL、代理日志和浏览器历史中:

securityDefinitions:
  api_key:
    type: apiKey
    name: X-API-Key
    in: header

具体支持方式以当前 ESP/ESPv2 配置为准。

计费统计应采用 operationId 或对应的方法名,不要直接按 URL 汇总。URL 可能带有路径参数、查询参数或版本前缀,不够稳定。

2. 建立客户与凭据的映射

可以在 BigQuery 中维护一张维表:

CREATE TABLE billing.client_credentials (
  credential_id STRING NOT NULL,
  client_id STRING NOT NULL,
  valid_from TIMESTAMP NOT NULL,
  valid_to TIMESTAMP
);

如果通过 consumer project 识别客户,则可以改为:

CREATE TABLE billing.client_projects (
  consumer_project_number STRING NOT NULL,
  client_id STRING NOT NULL,
  valid_from TIMESTAMP NOT NULL,
  valid_to TIMESTAMP
);

需要保留 valid_from 和 valid_to,因为 key 可能轮换,project 也可能被重新分配。如果只保存当前映射,历史账单的归属可能随之改变。

3. 将请求日志导出到 BigQuery

在 Cloud Logging 中创建 Log Router sink,把相关的 ESP/ESPv2 请求日志路由到专用的 BigQuery dataset。

过滤器需要根据实际日志编写。可以先在 Logs Explorer 中确认以下内容:

  • resource.type
  • 服务名称字段
  • API method 或 operation 字段
  • consumer project 字段
  • credential identifier 字段
  • HTTP 状态码
  • 请求时间
  • 是否存在重复记录

不要直接照搬未经验证的示例字段来生成正式账单。ESP、ESPv2、Cloud Run、GKE 和 Compute Engine 的日志结构可能不同。

如果平台日志中没有稳定的客户标识,后端应在认证完成后补写一条结构化计费事件。例如:

{
  "event_type": "billable_api_request",
  "request_id": "01J...",
  "client_id": "client_a",
  "operation_id": "api_esp2_foo",
  "status_code": 200,
  "billable": true,
  "event_time": "2026-08-01T10:15:30Z"
}

这类事件同样不能记录原始 API key。

4. 按月份汇总

假设规范化后的请求表包含以下字段:

event_time
request_id
credential_id
operation_id
status_code
billable

月度统计可以写成:

SELECT
  c.client_id,
  r.operation_id,
  COUNT(DISTINCT r.request_id) AS request_count
FROM billing.endpoint_requests AS r
JOIN billing.client_credentials AS c
  ON r.credential_id = c.credential_id
 AND r.event_time >= c.valid_from
 AND (c.valid_to IS NULL OR r.event_time < c.valid_to)
WHERE r.event_time >= TIMESTAMP('2026-08-01 00:00:00+00')
  AND r.event_time <  TIMESTAMP('2026-09-01 00:00:00+00')
  AND r.billable = TRUE
GROUP BY
  c.client_id,
  r.operation_id
ORDER BY
  c.client_id,
  r.operation_id;

结果可能如下:

client_a | api_esp1     | 1520
client_a | api_esp2_foo | 430
client_a | api_esp2_bar | 87
client_b | api_esp1     | 941
client_b | api_esp2_foo | 216
client_b | api_esp2_bar | 12

如果日志能够保证每个请求只有一条记录,可以使用 COUNT(*)。如果同一个请求可能在多个日志阶段重复出现,就必须使用稳定的 request_id 去重,或者只选取代表最终处理结果的日志类型。

使用 Monitoring API 的场景

Cloud Monitoring 中的 Service Runtime/Consumed API 指标可以用来:

  • 查看各项服务的请求趋势
  • 设置配额告警
  • 检测异常调用量
  • 与月度汇总结果交叉核对

如果对应的 monitored resource 提供了 credential_id、consumer project 或 method 维度,也可以通过 Monitoring API 按这些标签分组。不过,是否存在这些标签、标签的具体含义以及数据保留期限,都应以项目中实际可见的 metric descriptor 为准。

即使指标维度能够满足需求,仍建议把计费结果写入自己的 BigQuery 表。计费过程通常还要处理以下事项:

  • 固定统计周期和时区
  • 保存历史凭据映射
  • 请求去重
  • 排除不计费的状态码
  • 处理退款和补偿请求
  • 生成可重算、可审计的账单快照

计费口径必须提前确定

至少要明确以下规则:

  • 2xx 是否全部计费。
  • 4xx 中的业务校验失败是否计费。
  • 401、403 等认证失败通常不应计入某个有效客户的使用量。
  • 429 限流请求是否计费。
  • 5xx 是否免计费。
  • 客户端重试是否算作一次新调用。
  • 长时间请求超时后是否计费。
  • 批量 API 按 HTTP 请求数计费,还是按处理条目数计费。
  • 月份按 UTC 还是业务所在地时区划分。

如果按上海时区结算,应明确处理时区,不能直接按 UTC 日期截断:

SELECT
  DATE_TRUNC(DATE(event_time, 'Asia/Shanghai'), MONTH) AS billing_month,
  client_id,
  operation_id,
  COUNT(*) AS request_count
FROM billing.normalized_requests
WHERE billable = TRUE
GROUP BY billing_month, client_id, operation_id;

关键注意事项

  1. 不要把 API key 当作客户主键。key 会被轮换或吊销,一个客户也可能拥有多个 key。

  2. 不要记录原始 key。应使用 consumer project number、平台提供的 credential ID,或者服务端生成的不可逆标识。

  3. 区分”请求到达 ESP”和”业务成功完成”。如果收费依据是业务处理结果,只统计 ESP 收到的请求并不够,后端还需要写入最终计费事件。

  4. 为请求设置幂等标识。日志导出、代理重试或应用重复写入都可能产生重复数据。

  5. 同时保留原始事件和月度快照。原始数据用于重算,冻结后的月度结果用于对账,两者不能相互替代。

  6. 先核对实际日志字段。运行环境和配置不同,服务名、方法名、consumer project、credential ID 所在的字段也可能不同。

为每个客户分配一个 consumer project,适合隔离配额和消费归属。要可靠地按月、按客户和 ESP API 统计用量,还需要把 Cloud Logging 导出到 BigQuery,并配合客户映射、明确的计费规则和请求去重。

备注:内容仅供参考。