跳到内容

19.2 OpenAPI、gRPC、AsyncAPI 与契约演进

契约把路径、消息、字段、错误和兼容承诺变成可评审、可生成、可测试的制品。它不是实现自动导出的漂亮文档,而是生产者和消费者共同依赖的边界。

选择契约形式

场景常见契约关注点
HTTP 请求/响应OpenAPI路径、方法、参数、schema、安全和响应
高效服务 RPC/流Protocol Buffers + gRPC方法、消息字段号、deadline、状态与流语义
消息与事件 APIAsyncAPIchannel、operation、message、协议 binding 与 schema

协议选择取决于客户端生态、浏览器需求、流式交互、性能、治理和长期兼容,不存在对所有边界最优的一种协议。

OpenAPI 描述 HTTP 契约

yaml
openapi: 3.2.0
info:
  title: Tournament API
  version: 1.4.0
paths:
  /tournaments/{id}:
    get:
      operationId: getTournament
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200':
          description: Tournament found
        '404':
          description: Tournament not found

契约应在评审和 CI 中检查:唯一 operationId、认证声明、所有状态响应、分页与幂等约定、示例是否通过 schema 校验。

生成客户端能减少手写错误,但生成代码不能替代语义设计。SDK 还要处理重试、deadline、认证、可观测性和版本支持政策。

gRPC 契约保护字段号

proto
service ScoringService {
  rpc SettleMatch(SettleMatchRequest) returns (SettleMatchResponse);
}

message SettleMatchRequest {
  string match_id = 1;
  string winner_id = 2;
  string idempotency_key = 3;
}

Protocol Buffers 的字段号是线上身份。删除字段后应 reserved 原编号和名称,不能把编号分配给新语义:

proto
message SettleMatchRequest {
  reserved 2;
  reserved "winner_id";
  string match_id = 1;
  string idempotency_key = 3;
}

客户端应显式设置 deadline。DEADLINE_EXCEEDED 对写请求仍可能表示服务端已完成,必须结合幂等键或状态查询处理。

gRPC status code 应区分参数无效、前置条件失败、并发冲突、未认证、无权限和暂时不可用;不要把所有异常映射为 UNKNOWNINTERNAL

AsyncAPI 描述消息交互

消息契约不只包含 payload,还应说明:

  • channel/topic 与发布、订阅操作;
  • 事件 ID、聚合 ID、版本和时间字段;
  • 分区键与顺序范围;
  • schema 兼容策略;
  • 安全、协议 binding 和重试/死信约定;
  • 生产者和消费者所有者。

AsyncAPI 能描述结构,端到端投递、幂等和补偿仍需运行设计与测试。

“新增字段”也可能破坏消费者

结构上向后兼容的变更可能语义不兼容:

  • 新增枚举值会击穿客户端的穷举分支;
  • 原可选字段变成业务必填;
  • 数字单位从秒变毫秒;
  • 列表排序规则改变;
  • 以前不会为空的数组开始返回空;
  • 错误码含义或重试建议改变。

兼容性要同时检查 wire、source、behavior 和 data 四个层面。

版本策略

优先做兼容演进:新增可选能力、维持旧语义、给出弃用窗口。只有无法兼容时才引入新主版本或新资源。

版本可以放在路径、header、media type 或 schema/topic 名中。选择哪种不如以下问题重要:

  • 两个版本要并存多久;
  • 客户端如何发现弃用和迁移指南;
  • 生产者如何知道还有谁使用旧版;
  • 何时停止支持,谁批准;
  • 回滚时新旧数据是否兼容。

契约测试的三层门禁

  1. 静态兼容检查:OpenAPI/Proto/AsyncAPI diff 和 lint;
  2. 提供者验证:实现是否满足契约和示例;
  3. 消费者场景:关键消费者依赖的字段与交互是否仍成立。

消费者驱动契约适合捕捉真实依赖,但不能让每个消费者冻结提供者的内部设计。契约应围绕公开行为,避免断言无关字段和调用次数。

治理不是中央审批队列

有效治理提供自动化 paved road:模板、lint、兼容 diff、生成、测试、目录和弃用仪表盘。平台团队制定共通安全与可操作性规则,领域团队仍拥有业务语义。

每个契约都需要:所有者、稳定级别、认证方式、SLO、变更记录、弃用政策和支持渠道。

参考资料

Built with VitePress | Software Systems Atlas