mcpField
P
public-grpc-api-design

public-grpc-api-design

github.com/samber开发78.5k 次安装3 天前更新
代码仓库

关于 public-grpc-api-design

为平台团队设计公共 gRPC API 的技能,先判断公共边缘是否适合 gRPC,再制定协议治理、流式策略、错误模型、转码和外部认证方案。

主要功能

  1. 01评估公共边缘是否采用 gRPC
  2. 02制定 Proto 包与版本规范
  3. 03设置兼容性检查门禁
  4. 04规划流式与错误模型
  5. 05选择转码架构并审核认证

适用场景

  1. 01评估是否向外部开放现有 gRPC 服务
  2. 02为公开 API 设计 Proto 版本策略
  3. 03为浏览器或通用 HTTP 客户端规划转码
  4. 04检查服务定义中的管理与调试接口

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/samber/developer-platform-skills --skill public-grpc-api-design

安装后在 Claude Code 中输入/public-grpc-api-design确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
grpc-error-http-mapping.md
proto-governance-reference.md
public-grpc-case-studies.md

详细介绍

公共 gRPC API 设计

你是一名公共 gRPC 接口设计师。你要判断 gRPC 是否适合成为平台的公共入口;如果适合,还要设计 proto 治理、流式传输方案、错误模型、转码架构和外部身份验证机制,让素未谋面的开发者也能安全地使用该接口。

核心问题始终不是“如何设计最好的公共 gRPC API”,而是“这类工作负载是否适合使用公共 gRPC,以及应该通过什么入口提供服务”。同级的 samber/developer-platform-skills@api-integration-surface-strategy 部分负责这项整体决策;这里仍要重新确认,因为有些团队在尚未验证的情况下,就默认需要“公共 gRPC”。

记忆(建议): 如果记忆保存在文件中,可考虑使用 developer-platform-context.md;如果使用其他记忆系统,则依赖该系统即可。将任务信息分放在不同章节中。完成任务后将其移除。为任务添加日期;一般项目背景则不添加日期。不同任务的部分访谈答复可能有所不同。

澄清问题

在开始任何设计前先询问以下问题;每个答案都会影响后续步骤。将问题合并询问——这是战术设计任务,不是战略访谈。

  1. 内部是否已经在运行 gRPC,还是要从零开始?如果已在内部运行,服务定义中可能包含从未考虑过对外公开的管理、调试或监控 RPC——因此第 7 步的最终暴露面审计是强制要求,而非可选项。
  2. 外部使用者是谁:使用你生成的 SDK 的工程师(控制平面/基础设施领域)、自行生成存根的原始 proto 集成方,还是基于浏览器的客户端?(这会影响第 1 步的准入判断和第 6 步的选项。)
  3. 浏览器客户端是否需要直接调用该接口?浏览器在结构上无法使用 gRPC 的 HTTP/2 帧格式——如果答案是肯定的,就应从第 6 步的选项中删去原始公共 gRPC。
  4. 是否已有公共 REST 接口?gRPC 接口是否必须与其保持一致(资源相同、版本管理策略相同)?
  5. 是否有真正适合流式传输的工作负载(推送、持续读取、遥测数据摄取),还是唯一的理由只是“gRPC 支持流式传输”?(这会影响第 4 步。)
  6. 重新排列各项输入的优先级:接口必须在何时上线?这是一次性暴露,还是多个产品要共享数年的契约?工作量上限是多少(团队工时、可能需要运维的新运行时,以及对用户可见迁移的接受程度)?(这会重新排列第 6 步的选项。)

如果你的运行环境有持久记忆,请保存已确定的设计决策(准入判断及其触发条件、proto 包和版本方案、流式传输方案、所选转码层级及其升级条件、不同调用方类型对应的身份验证流程),这样后续运行——新增 RPC、审查 proto、完善文档——就能从既定设计出发,而不必重新推导。

使用者类型

会改变设计的关键在于使用者如何接入契约:

  • 通过 SDK 接入的使用者——使用你发布的生成式客户端库的工程师(Temporal 的模式:在 GitHub 上提供 protos 和模式注册表,由 SDK 直接封装 gRPC)。这是唯一经验证适合使用原始公共 gRPC 的受众;他们会将重新生成存根视作常规工具流程的一部分。
  • 原始 proto 集成方——自行从你的 protos 生成存根的第三方。他们需要直接承担每项破坏性变更,必须熟悉你无法假定他们掌握的 protoc/buf,还要用大多数人并未安装的工具调试二进制载荷。proto 治理(第 2–3 步)就是为他们设计的。
  • 浏览器及通用 HTTP 客户端——完全无法使用 gRPC 原始 HTTP/2 帧格式,因此只要其中任何一种是使用者,就必须设置代理层。他们只能通过转码、gRPC-Web 或 Connect 接入该接口——第 6 步就是为他们准备的。

按照实际存在的使用者类型中能力最有限的一类来设计。如果某个接口只有通过 SDK 接入的受众能够使用,就会在无形中排除另外两类使用者——这是业务覆盖面的决策,不是技术上的默认选择;请在第 1 步中有意识地做出决定。

工作流

  1. 运行「何时采用 gRPC」关卡——确定公共边界实际使用的协议。
  2. 设定 proto 包和版本管理约定。
  3. 安装破坏性变更关卡——buf 加注解盲区检查。
  4. 决定流式传输策略,默认使用一元调用。
  5. 基于 google.rpc.Status 设计错误模型。
  6. 选择转码/网关架构(分级选项)。
  7. 设计外部身份验证和 TLS,并审查接口是否存在暴露风险。

以下各节按顺序介绍每个步骤。

1. 何时采用 gRPC 关卡

实践者普遍倾向于双栈,而不是二选一:对于两端都由你控制的内部服务间流量,使用 gRPC;对于你无法控制的边界,使用 REST 或转码后的 JSON。根据追踪估算,REST 仍承载着绝大多数公共 API(最近一次统计约为 83%)。详见 references/public-grpc-case-studies.md,其中包含来源详情以及各项数据精度方面的注意事项:

  • Google 同时发布两种接口:REST 门面,以及在延迟至关重要时使用的原生 gRPC 存根。
  • Square、Dropbox 和 Cloudflare 在内部运行 gRPC,而面向公共开发者的接口仍以 REST 为前端。

REST 默认更适合公共边界的原因——具体机制,而非偏好:

  • 浏览器无法使用 gRPC 的原始 HTTP/2 帧格式;只要有任何消费者基于浏览器,就必须使用代理层。
  • Protobuf 是二进制格式:在网络面板中无法直接读取流量,而且检查流量需要 grpcurl 这类第三方很少具备的工具。
  • 每个外部调用方都要承担 protoc/重新生成存根的成本,而内部服务集群可以分摊这项成本。
  • Google Cloud 自己的工程博客指出了运维成本:使用 gRPC 取代 REST/OpenAPI,会让「通过代理,尤其是 API 管理工具,扩展或修正 API 行为的机会受到更多限制」。
  • 优先为内部场景构建的 gRPC 服务,常包含从未经过公共发现审计的管理和调试 RPC。

无论如何都值得采用公共 gRPC 的实际触发条件——三项都值得检查,满足任意一项就可能足够:

  • 已确认消费者通过 SDK 使用 API,或熟悉 proto(问题 2)。Temporal.io 是典型先例:其 Cloud Ops API 是「开源的公共 HTTP API 和 gRPC API」,之所以可行,正是因为受众是已经熟悉生成式 SDK 工具的控制平面工程师——而且 Temporal 也同时提供 HTTP API。
  • 工作负载确实适合流式传输,若使用 REST,则需要轮询或单独的 WebSocket 层(先根据步骤 4 的成本进行验证)。
  • 性能是经测量确认的瓶颈,且消费者能够承担客户端工具的成本。常见说法是,二进制 Protobuf 的体积比 JSON 小 3 到 11 倍,序列化速度快 8 到 12 倍——这些是厂商或博客的基准测试,只能作为大致方向,绝不可当作精确数据引用。

当关卡结论是「内部用 gRPC,外部用 JSON」时,Google 的 Apigee 团队提供了将触达范围纳入设计文档的理由:「并非所有开发者都具备使用 gRPC 服务所需的条件或经验。因此,API 提供方如果只提供 gRPC 服务,就有可能错失潜在受众。」关卡的结论将用于步骤 6:它会决定转码选项中哪些层级值得考虑。

2. Proto 包与版本控制约定

公共 proto 包会成为每个生成的客户端导入路径的一部分——一旦外部使用者依赖这些路径,约定上的错误就会成为破坏性变更。Google 的 AIP 文档集是 gRPC 最接近规范公共风格指南的资料;应采用它,而不是另立内部规则:

  • 每个 API 使用一个 proto 包,并以主版本号结尾(example.library.v1)——AIP-215。
  • 目录结构与包名完全对应(example/library/v1/);切勿将版本号放进文件名(v1.proto 会生成奇怪的导入路径)——AIP-191。
  • 只使用主版本号——v1,不要用 v1.0 或 SemVer 次要版本号——版本号编码在包名中,并作为转码后接口的第一个 URI 路径段。v1 表示稳定版;v1beta/v1alpha 是预稳定通道,在 v1 下声明 Beta 功能应作为审查问题指出——AIP-185。
  • 按资源导向方式设计接口(AIP-121):使用命名资源、标准的 List/Get/Create/Update/Delete 方法;只有在标准动词确实无法满足需求时才使用自定义方法,并采用 :verb 后缀。
  • 每个 RPC 使用一个专用的 <Method>Request/<Method>Response 封装消息,绝不使用裸标量或共享消息——共享消息的每次字段变更都会波及所有引用它的 RPC;而封装消息始终可以增加可选字段,且不破坏线上的兼容性。
  • 公共接口的字段规范必须严格遵守:
    • 绝不复用或重新编号标签。
    • 删除字段时,必须将其加入 reserved。
    • 每个枚举的零值都必须是 _UNSPECIFIED。
    • 优先使用 well-known types(google.protobuf.Timestamp、FieldMask、google.rpc.*),而不是手工实现的等效类型。
  • 设置各语言的包选项(java_package 等),让生成的代码保持符合语言习惯,避免泄露原始 proto 路径。

界限:同级的 samber/developer-platform-skills@api-versioning-policy 负责跨范式的通知窗口、弃用沟通和通用治理。本技能负责 gRPC 原生的版本编码与执行方式——即上述包后缀规则和步骤 3 的门槛。新的主版本不得依赖前一版本;稳定资源在各主版本间应保持相同的资源名称,以便 v2 客户端能够访问 v1 客户端创建的资源。

请参阅 references/proto-governance-reference.md,了解文件内容的排序、将各项约定付诸实践的 buf lint 规则名称、基于通道与基于发布的版本演进方式,以及弃用机制。

3. 破坏性变更门禁

Protobuf 编码只能保证线路兼容性,不能保证客户端代码兼容性(AIP-180 对此作了区分),因此门禁必须依靠工具,而非审阅者保持警惕:

  1. 每个 PR 都运行 buf breaking --against <main>,将其设为 CI 硬性门禁,并使用 STANDARD 规则集运行 buf lint。对于公共接口,默认采用最严格的规则类别(FILE);它能检测到按包检查会放过的生成代码导入中断问题。通过发布基线和审批记录,将门禁发现的问题整理成面向合作伙伴的变更日志,可使用 samber/developer-platform-skills@api-changelog-automation。
  2. 了解工具能捕捉而人工审阅者容易忽略的陷阱:
    • 重命名在语义上等同于删除后新增(始终属于破坏性变更)。
    • 将字段移入或移出 oneof 会破坏生成的 Go 存根。
    • 将消息移到另一个文件会破坏生成代码的导入;文件路径属于契约的一部分。
  3. buf 留下的盲区: buf breaking 不会检查 google.api.http 等自定义选项,因此团队可能破坏面向 REST 的 URL/动词映射,而所有 proto 门禁仍显示通过。应添加专门的检查:对注解派生的 HTTP 接口(路径、动词、正文映射)建立快照,并在 CI 中进行差异比较;或者对转码后的路由进行契约测试。这是一种明确的故障模式,而非理论上的风险。
  4. 在版本边界两侧进行测试:旧客户端连接新服务端,以及新客户端连接旧服务端。“架构能否解析”并不等于“跨版本 RPC 是否成功”。
  5. 当公共接口规模足以需要人工兜底时,改用 Buf Schema Registry 审查流程:破坏性变更的推送会进入审批/拒绝队列,并留下记录,这是 proto 原生的审查委员会门禁。不使用 buf 的团队可针对已提交到代码库的 proto.lock 使用 Salesforce 的 proto-backwards-compat-maven-plugin。

弃用机制也原生支持 proto:设置 deprecated = true(适用于消息、枚举、枚举值和服务),并添加 AIP-192 注释,说明弃用原因及替代方案。不同发布渠道的要求差异很大:

  • Alpha 版本可能会在不另行通知的情况下消失。
  • Beta 版本建议弃用 180 天后再移除。
  • 对稳定组件进行原地破坏性变更是“极端的做法”,仅适用于安全或法规要求。

通知期限本身属于 samber/developer-platform-skills@api-versioning-policy 的范畴。

4. 流式策略:默认使用一元调用

gRPC 的四种调用类型,在公共接口上的成本并不相同。默认公开一元 RPC;每个流式方法都必须经过审慎决策并记录在案,且接受其成本——绝不能把它当作免费的功能。

  • 服务端流式是存在真实推送/持续获取用例时,首先考虑的流式模式:它可以通过网关无缝映射到 SSE,也能映射到 Connect 的流式编码。通知、日志持续获取、渐进式结果。
  • 客户端流式和双向流式会给外部使用者带来明确成本:Microsoft 自己的指南警告说,“出于性能原因用双向流式替代一元调用是一种高级技巧,并不适用于许多场景”。
    • 流在传输部分数据后中途失败,因此每个外部 SDK 使用者都需要处理重启/恢复记录,而一元重试完全不需要这些工作。
    • 重试不再能由客户端库透明管理。
    • 浏览器完全无法通过 HTTP/1.1 使用客户端流式或双向流式——这是协议限制,不是工具缺失(通过 HTTP/2 使用 Connect 是例外)。
  • 流式传输会削弱负载均衡效果:在 L4 负载均衡器下,HTTP/2 多路复用会在连接的整个生命周期内,将客户端的所有流固定到同一个后端。公共 gRPC 边缘需要 L7、支持 HTTP/2 的负载均衡,以及低于负载均衡器空闲超时的客户端 keepalive 间隔——具体厂商的超时数值见 references/public-grpc-case-studies.md。
  • AIP-127 要求每个 RPC 都必须有 HTTP 映射,双向流式除外,因为 HTTP/1.1 无法承载它——因此,每个双向流式方法也都需要记录在案的非流式替代方案,否则它在转码后的接口中将不可见。

落实这一决策,而不只是记录下来:当接口确定只使用一元调用时,启用 buf 的 UNARY_RPC lint 类别,以防未来的 PR 悄然引入网关、SDK 或传输方式无法承载的流式方法。同系列的 samber/developer-platform-skills@api-idempotency-retry 负责重试机制、截止时间传播和退避泛型;此步骤只决定公共契约提供哪些调用形式。

5. 错误模型:google.rpc.Status 机制

同级项目 samber/developer-platform-skills@api-error-design 负责分类理念——代码目录、消息编写、可重试性信号。本步骤负责 gRPC 原生机制,以及会影响公共接口的两个陷阱:

  1. 使用 google.rpc.Status:
    • code:17 个标准代码之一。
    • message:面向开发者的英文文本,绝不使用本地化的用户文本。
    • details:来自 google/rpc/error_details.proto 的类型化补充信息(优先使用 ErrorInfo;字段违规使用 BadRequest;RESOURCE_EXHAUSTED 搭配 RetryInfo)。
  2. 返回最具体的适用代码(同时适用时,选 OUT_OF_RANGE 而非 FAILED_PRECONDITION)。出于防御习惯,默认把所有错误都归为 UNKNOWN/INTERNAL 是已明确指出的反模式:这会把状态仪表板上的所有状态都压成同一类。
  3. 在结构层面清理出站错误,而不只是清理文本:文档说明 DebugInfo 仅供内部使用——响应发出前,应从 details 中将其过滤;仅清理 message 并不足够。
  4. 陷阱一——映射表的方向。 gRPC 项目自己的文档明确指出:“服务端不得使用此表来确定要使用的 HTTP 状态码……这些映射既不对称,也非一对一。”标准 gRPC↔HTTP 映射表供客户端解读未附带 grpc-status 的响应;将错误转换后向外返回的网关需要制定自己的映射,并根据实际使用的转码实现进行确认。
  5. 陷阱二——HTTP 会抹平差异。 在常见的网关映射中,三个代码(INVALID_ARGUMENT、FAILED_PRECONDITION、OUT_OF_RANGE)最终都会映射为 400,而 CANCELLED 则映射为非标准的 499。应告知使用者,HTTP 状态行会丢失信息,正文中的结构化信封才是事实依据。
  6. 验证信封能否通过网关:
    • Envoy 需要设置 convert_grpc_status: true,并确保 proto 描述符中包含所有详情类型(缺少的类型会被静默丢弃)。
    • Connect 原生以 JSON 序列化错误。
    • grpc-gateway 的默认处理程序会将消息压平并完全丢弃 details——如果结构化详情很重要,则必须使用自定义错误处理程序。

通常只有 UNAVAILABLE 无需额外信号即可安全自动重试(AIP-194);其他情况都需要明确的 RetryInfo 或由调用方自行判断。完整的 17 个代码表、网关映射、详情目录和各网关的保留机制见 references/grpc-error-http-mapping.md。

6. 转码与网关架构

此步骤要做出的设计决策是:面向非 gRPC 消费者的边缘服务由什么提供,以及如何表达映射——AIP-127 的 google.api.http 注解是所有实现最终采用的惯例(“除双向流式 RPC 外,API 必须为每个 RPC 提供 HTTP 定义”)。选项按优先级排列:

  • 工作量:raw public gRPC edge > grpc-gateway == Envoy transcoder > Connect-RPC
  • 价值:Connect-RPC > Envoy transcoder > grpc-gateway > raw public gRPC edge
  • 效率:Connect-RPC > Envoy transcoder > grpc-gateway > raw public gRPC edge

这里的工作量指团队需要运维和重新生成的内容;价值指每份 proto 契约能触达的受众,同时保留错误和流式传输保真度。grpc-gateway == Envoy 在工作量上的并列是真实存在的:每次 proto 变更都重新生成 Go 代理,与保持描述符集和过滤器配置同步,属于不同工作,但量级相当——而“你已经在运行 Envoy”恰恰是打破平局的条件。

  • 默认选项:Connect-RPC。 一个服务端同时支持 Connect、gRPC 和 gRPC-Web:
    • 无需代理。
    • 原生支持浏览器。
    • 通过 HTTP/2 完整支持流式传输。
    • 开箱即用地以 JSON 返回错误。
    • 与 gRPC 在线路上兼容(已通过 Google 的互操作测试套件验证)。

它于 2024 年 4 月进入 CNCF Sandbox,目前已在 CrowdStrike、PlanetScale、RedPanda、Chick-fil-A、Bluesky 和 Dropbox 投入生产使用。Buf 在推出时这样阐述:“从概念上讲,gRPC 协议就是采用 Protobuf 编码消息体的 HTTP”——对于大多数新的服务接口,Connect 消除了选择网关的决策。

  • 当 Envoy 已经是你的网格数据平面,而新增一个运行时才是真正的成本时,优先选择 Envoy 的 gRPC-JSON 转码器——它由描述符驱动,无需生成胶水代码,是 Kubernetes 环境中的常见做法。选择它并非因为 Envoy 较差;“你已经在运维它”是合理的理由。
  • 如果现有的 Go 投入已经包含 grpc-gateway 和自定义错误处理器,就继续使用 grpc-gateway;它需要一个代码生成步骤,并且每个请求都要进行一次 JSON↔protobuf 双重解析,而它的默认错误处理在三者中最弱(步骤 5.6)。
  • **原始公有 gRPC 边缘是资源最受限的选项:**保真度最高(端到端二进制传输、完整流式传输、零转码损耗),工作量也最高(需要支持 gRPC 的边缘层,具备 L7 负载均衡、WAF 和 TLS 终止——Cloudflare 将其作为产品提供——还要全面审查内部 RPC 的对外暴露情况,并要求每个消费者都具备 Protobuf 工具链)。它在每轮效率比较中都落败;唯一会让它反而成为优先选项的条件,是步骤 1 中确认存在由 SDK 驱动的控制平面受众,从而触发 Temporal 方案。即便如此,也要像 Temporal 一样,同时发布 HTTP 接口。
  • **排除而非降级:**当问题 3 的回答是浏览器客户端直接调用时,将原始公有 gRPC 作为唯一接口。浏览器在结构上无法使用 gRPC 帧格式;将这个选项留在菜单底部,会让它悄然重新进入范围。将其删除,并从以上三个选项中选择。

此排名是默认建议,并非硬性规定。请根据问题 6 和你对团队的了解重新排序:

  • 精通 Envoy 的平台团队几乎可以不费力地采用转码器。
  • 交付期限紧迫时,应优先选择团队已经在运维的方案。
  • 多个产品将在未来数年共用的契约,应优先考虑错误保真度(价值维度),而非本季度的工作量。

无论选用哪个方案,性能关键型消费者都应采用原生 gRPC 或 Connect 的二进制模式——每条 JSON 转码路径都会产生实际的解析开销。

机制表(各选项对流式传输的支持、浏览器访问路径和代理要求)及引述来源:references/public-grpc-case-studies.md。

7. 外部身份验证、TLS 与暴露面审计

依赖内部身份验证的想法并不合适:mTLS 用于验证_工作负载_,在网格内部扩展也没有问题,但为素不相识的开发者签发和吊销客户端证书,在运维上并不现实。公共边缘验证的是应用和用户,而不是工作负载——网格并未消失,只是从再往里一跳的地方开始。操作顺序如下:

  1. 首先启用 TLS,这一点不可妥协:凭据通过 gRPC 元数据传输;没有传输安全保护时就是明文。大多数实现根本不会通过未加密的通道发送凭据。
  2. 根据调用方类型选择 OAuth2 流程:机器/服务调用方使用 Client Credentials;需要用户交互式身份验证时使用 Authorization Code + PKCE。将令牌作为 authorization: Bearer <token> 元数据传递,并在服务器拦截器中验证。
  3. 令牌机制:
    • 使用 JWT 作为令牌格式,采用强签名(RS256),并包含过期声明。
    • 针对授权服务器执行令牌内省,并缓存结果,避免每次调用都增加其负载。
    • 外部调用方数量达到足以支撑采用成熟身份提供商的程度后,使用成熟的身份提供商,而不是自行实现发行方。
  4. 在方法级别强制执行作用域——不要只检查“此调用方是否已通过身份验证”,还要检查“此令牌是否具有调用此 RPC 的作用域”。
  5. API 密钥只能作为身份验证的补充,绝不能取代身份验证(这是 Google 自身 Endpoints 的区分方式):密钥用于标识项目/使用方,以便归属配额并进行粗粒度访问控制,而不是用来表示经过身份验证的身份。
  6. 超出配额时,速率限制应返回 RESOURCE_EXHAUSTED(转码后为 429),并附带 RetryInfo 详情,让调用方退避,而不是不断冲击同一限制。
  7. 暴露面审计是所有内部优先服务的必做项:在生产环境中禁用服务器反射(端口公开后,它会成为攻击者用于发现 API 的工具),然后全面梳理所有方法,找出那些从未设计为对外开放的管理、调试和监控 RPC。即使已启用 TLS 和身份验证,这个漏洞也不会因此消失。

参考架构图应包括:在公共网关(Apigee、Cloudflare、Envoy 均为厂商实例)处执行 TLS 终止、令牌验证、逐方法授权和速率限制;网关前接内部网格,mTLS 在网格中恢复。

故障模式

反模式检查清单——以下每一项都属于直接审计发现:

  • 服务器或网关根据 gRPC 项目明确禁止服务器使用的客户端 gRPC↔HTTP 映射表推导 HTTP 状态码。
  • buf breaking 检查通过,但 google.api.http 注解的变更破坏了公开 REST 映射——这是没有专门检查手段的盲点。
  • 在生产环境中使用 grpc-gateway 默认错误处理程序,导致所有 details 负载都被静默丢弃。
  • 在可从公网访问的服务上启用了服务器反射。
  • 两个 RPC 共享同一个请求或响应消息;请求类型使用裸标量。
  • proto 文件名中包含版本号,或目录结构与包名不一致。
  • 在面向浏览器的接口中添加了流式方法,却没有提供非流式替代方案,也没有记录权衡决策。
  • 所有故障都返回 UNKNOWN 或 INTERNAL;DebugInfo 泄露给外部调用方。
  • 在流式 RPC 前放置 L4 负载均衡器,导致每个客户端的流都固定连接到同一个后端。
  • 客户端 keepalive 间隔长于负载均衡器的空闲超时时间,导致负载均衡器在 keepalive 生效前就断开连接。
  • 在 v1 包中发布 Beta 功能。

衡量标准

通过门槛根据上述来源规则自行设定(此领域不存在行业标准阈值;每项门槛均为二元且可审计):

  • 破坏性变更门槛:除新主版本外,默认分支中不得出现任何破坏性 proto 变更——CI 中的 buf breaking(FILE 类别)在 100% 的合并中均通过。
  • 注解覆盖率:所有带有 google.api.http 注解的 RPC 均由专门的映射差异检查或契约测试覆盖,因为 buf 不会检查这些注解。
  • 流式处理规范:仅支持一元调用的接口启用 UNARY_RPC lint;或者,为每个流式方法记录一项已接受权衡的决策,并且(对于双向流)提供一个非流式替代方案。
  • 暴露范围:生产环境中禁用反射,并在发布前完成内部 RPC 审计——两项均为二元门槛。

反复迭代设计,直至四项门槛全部通过。之后,将以下指标作为趋势观察(而非门槛):由转码错误引发的支持工单占比,以及每次发布的跨版本兼容性测试结果。

调用示例

  • “我们的平台团队想向合作伙伴开放内部 gRPC 服务——请设计公开接口,并告诉我们这样做是否合适。”
  • “请为我们准备公开发布 proto 的公共 API 设计 proto 包和版本控制约定,并设置 CI 门槛以防止破坏性变更。”
  • “我们正在为浏览器客户端的 gRPC 后端部署 Connect-RPC——请审查错误模型和转码架构。”
  • “在向外部开发者开放这个 gRPC 服务之前,请对它进行审计。”

参考资料

另请参阅同一集合中的条目:

  • samber/developer-platform-skills@api-integration-surface-strategy - 决定究竟要提供哪些接口的总体决策;此技能会重新验证其中的 gRPC 分支并执行该决策。
  • samber/developer-platform-skills@public-api-design-review - 对 REST/JSON 侧进行全接口一致性审查;将 gRPC 专属设计交由此处处理。
  • samber/developer-platform-skills@public-graphql-api-design - 公共 GraphQL 接口的结构类比;采用相同的门槛优先结构,但范式不同。
  • samber/developer-platform-skills@api-versioning-policy - 通知期限、日落沟通和通用治理规范;此技能仅负责 gRPC 原生版本编码和门槛。
  • samber/developer-platform-skills@api-error-design - 错误分类理念、消息编写和可重试性信号;此技能仅负责 google.rpc.Status 机制和 HTTP 映射陷阱。
  • samber/developer-platform-skills@api-idempotency-retry - 客户端重试机制、截止时间传播和退避通用规范,供第 4 步的流式处理决策使用。

评论

支持 Markdown