Files
story/docs/observability-and-api-governance.md
T

69 lines
6.0 KiB
Markdown
Raw Normal View History

2026-08-26 06:41:07 +08:00
# 可观测性与接口治理设计
## 1. 目标与适用范围
本设计作为 API、Callbacks 与 Worker 三个进程的公共能力基线,统一请求关联、分布式追踪、指标采集、流量保护、列表分页和资源并发更新规则。领域接口不得另行定义不兼容的分页游标或版本标记。
OpenAPI是浏览器、设备和回调接口的事实来源。拉取请求以目标分支契约为基线执行破坏性变更检查;设备接口还由代码测试固定`/device/v2`主版本、禁止以`User-Agent`推测能力,并要求兼容说明与OpenAPI版本同步。设备同主版本的新增行为必须通过可选能力位协商,破坏性变化使用新主版本,具体规则见[设备API兼容性约定](device-api-compatibility.md)。
## 2. 请求关联与分布式追踪
- 每个 HTTP 请求接收或生成 `X-Request-Id`,并在响应、统一错误体和结构化日志中返回。
- `otelhttp` 在最外层创建服务端 Span,接收并传播 W3C `traceparent` 与 `baggage`。
- 访问日志记录 `request_id`、`trace_id`、`span_id`、服务、方法、匹配后的路由模板和耗时;不记录 Authorization、Cookie、录音正文或供应商密钥。
- 本地环境通过 OTLP/gRPC 发送至 OpenTelemetry Collector。Collector 当前使用调试导出器,后续环境可替换为 Tempo、Jaeger或云厂商后端,不改变业务代码。
## 3. Prometheus 指标
| 指标 | 标签 | 用途 |
|---|---|---|
| `voice_platform_http_requests_total` | service、method、route、status | 请求量与错误率 |
| `voice_platform_http_request_duration_seconds` | service、method、route | HTTP延迟分布 |
| `voice_platform_http_inflight_requests` | service | 在途请求数 |
| `voice_platform_dependency_up` | service、dependency | PostgreSQL、Redis最近一次检查状态 |
| `voice_platform_dependency_check_duration_seconds` | service、dependency | 依赖检查耗时 |
| `voice_platform_outbox_events` | status | pending、processing、dead积压 |
`/metrics`与`/health/*`不进入业务限流。Prometheus 每10秒抓取三个进程;本地入口为 `http://localhost:59190`。生产环境应限制指标端点只允许监控网络访问。
API还会按`EXTERNAL_PROBE_INTERVAL`周期验证OIDC JWKS端点可读取、JSON结构有效且至少存在一把公钥。启用方舟的API/Worker会对`ARK_BASE_URL`执行证书校验TLS握手,启用火山RTC的Worker会对`VOLC_RTC_BASE_URL`执行同类握手,启用托管内容安全的Worker会对`CONTENT_SAFETY_ENDPOINT`执行同类握手。TLS探针只做DNS、TCP和TLS协商,不发送供应商业务请求或待分类正文,因此不消耗模型Token、RTC分钟、策略调用或任务配额。
探针结果复用`voice_platform_dependency_up{service,dependency}`,其中外部标签为`oidc_jwks`、`ark_tls`、`volc_rtc_tls`和`content_safety_tls`。失败不会改变服务readiness;模型和内容安全调用本身已有降级或安全重试路径,供应商网络故障不应触发Pod重启风暴。`PlatformExternalDependencyUnavailable`只在指标连续5分钟为0后触发CRITICAL告警。运营验收快照可把四类依赖全部选为必需项,任何记录缺失、不可用或超过三倍探针周期未更新都会阻断技术门禁。
## 4. Redis令牌桶限流
- Key格式:`voice-platform:ratelimit:{scope}:{identifier_hash}`。
- 标识符使用SHA-256截断摘要,Redis中不保存原始IP、用户编号、设备编号或客户组织编号。
- Lua脚本以Redis服务器时间原子完成补充、扣减和过期时间设置,避免多实例竞态及应用服务器时钟偏差。
- HTTP入口当前先启用 `network` 范围;应用服务可复用同一组件增加 `user`、`device`、`tenant` 范围并组合更严格策略。
- 超限返回HTTP 429、错误码 `RATE_LIMITED`、`Retry-After` 和 `X-RateLimit-Remaining`。
- Redis短时故障采用可观测的 fail-open,避免公共缓存故障放大成全站中断;登录、设备注册、密钥操作等高风险接口落地时应叠加本地保护或按接口改为 fail-closed。
本地默认速率为每来源50次/秒、突发100次,可通过 `RATE_LIMIT_RPS` 和 `RATE_LIMIT_BURST` 调整。生产值必须依据容量测试冻结。
## 5. 游标分页规范
- 查询参数 `limit` 缺省为20,只允许1至100。
- `cursor` 是HMAC-SHA256签名的不透明Base64URL值,载荷只含版本、稳定排序键和公开资源ID。
- 服务端按“排序键 + ID”组成确定性顺序,查询下一页时使用严格大于或小于条件,禁止把数据库偏移量作为游标。
- 响应中的 `next_cursor` 仅在确有下一页时返回;客户端不得解析、修改或长期持久化游标。
- 签名失败、版本不支持或字段缺失统一作为无效游标处理,不把解析细节暴露给调用方。
## 6. ETag与乐观并发规范
- 可变资源读取成功时返回强ETag,格式为 `"v{version}"`,例如 `"v3"`。
- PATCH、PUT和状态变更请求必须携带 `If-Match`;缺失时映射HTTP 428,格式无效映射HTTP 400,版本不一致映射HTTP 412。
- 不接受弱ETag `W/` 或通配符 `*`。
- 数据表须包含从1开始的 `version` 列;更新SQL必须带 `WHERE id=? AND version=?`,成功后原子执行 `version=version+1`。
- ETag只解决并发覆盖,不替代幂等键。会产生外部副作用的写请求仍须使用幂等执行器和outbox。
## 7. 验证门禁
1. 日常开发运行变更包测试、OpenAPI类型生成和前端构建;`go test ./...`、`go vet ./...`保留给CI或发布候选。
2. 游标篡改、非法limit、弱ETag、缺失If-Match和版本冲突必须有自动化测试。
3. Redis集成测试必须证明突发上限和令牌恢复。
4. 三个后端 `/health/ready` 返回200,Prometheus三个target均为 `up=1`。
5. Collector日志出现三个服务上报的Span,访问日志出现对应 `trace_id` 与 `span_id`。
6. Worker指标能区分待处理、处理中和死信事件。
7. OpenAPI破坏性变更检查和设备协议兼容门禁必须通过。