如何按 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;
关键注意事项
-
不要把 API key 当作客户主键。key 会被轮换或吊销,一个客户也可能拥有多个 key。
-
不要记录原始 key。应使用 consumer project number、平台提供的 credential ID,或者服务端生成的不可逆标识。
-
区分”请求到达 ESP”和”业务成功完成”。如果收费依据是业务处理结果,只统计 ESP 收到的请求并不够,后端还需要写入最终计费事件。
-
为请求设置幂等标识。日志导出、代理重试或应用重复写入都可能产生重复数据。
-
同时保留原始事件和月度快照。原始数据用于重算,冻结后的月度结果用于对账,两者不能相互替代。
-
先核对实际日志字段。运行环境和配置不同,服务名、方法名、consumer project、credential ID 所在的字段也可能不同。
为每个客户分配一个 consumer project,适合隔离配额和消费归属。要可靠地按月、按客户和 ESP API 统计用量,还需要把 Cloud Logging 导出到 BigQuery,并配合客户映射、明确的计费规则和请求去重。
备注:内容仅供参考。
