其他
设计/实现 gRPC 与 Protobuf 服务时,用契约…
适用于设计 RPC 服务、定义 protobuf schema、实现流式 API、配置 gRPC 拦截器或构建跨语言服务通信。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 46/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 42/100 ×0.15
适用场景
设计/实现 gRPC 与 Protobu通用
解决思路
- 定义服务契约:按照下方命名与风格约定编写 .proto 文件,包含 service 定义、RPC 方法和 message 类型。
- 生成语言 stub:使用合适的语言插件运行 protoc(例如 protoc-gen-go-grpc、grpcio-tools),生成服务端和客户端代码。
- 实现服务端:为每个 RPC 方法创建 handler 函数,注册到 gRPC server,并配置 TLS、拦截器和健康检查。
- 实现客户端:创建到服务端的 channel,实例化生成的客户端 stub,并在调用 RPC 方法时设置正确的 deadline 和错误处理。
- 添加拦截器:接入服务端和客户端拦截器,用于日志、认证、指标和追踪。
- 编写测试:用 mock 依赖对 handler 做单元测试;用真实 gRPC 连接做集成测试。
- 部署与观测:上生产前启用分布式追踪(OpenTelemetry)、结构化日志和指标看板。
- 使用 Protocol Buffers 同时作为接口定义语言(IDL)和消息交换格式。
- 围绕‘定义可远程调用的方法及其参数和返回类型’来设计服务。
- 优先保证类型安全、性能和向后兼容。
- 实现中不要留下任何 todo、占位符或缺失部分。
- 按 1-1-1 模式组织定义:每个 .proto 文件只放一个顶层实体(message、enum 或 extension)。
- 让每个 .proto 文件对应一条构建规则。
- 以此保持 proto 定义小而模块化。
- 以此获得简化重构、缩短构建时间和减小二进制体积的收益。
- 使用结构化 message 提升可扩展性——Protocol Buffers 支持在不破坏现有客户端的情况下添加字段。
- 在以后可能添加字段的地方谨慎使用 structs。
- 不要在不同 RPC 之间复用 message——API 可能随时间变化,避免把独立 RPC 调用紧密耦合。
- 字段之间应始终相互独立——不要让一个字段影响另一个字段的语义。
- 使用描述性字段名,并采用 underscore_separated_names。
- 为删除的字段保留字段编号,防止未来冲突。
- 对不总是存在的字段使用 optional。
- 当用户需要在互斥选项中选择时,考虑使用 oneof。
- 确保第一个枚举值始终为 0。
- 使用 UNSPECIFIED 默认值(例如 STATUS_UNSPECIFIED = 0)。
- 使用前缀避免命名冲突(例如 ORDER_STATUS_CREATED 与 STATUS_PENDING)。
- 为移除的枚举值保留编号,防止意外复用。
- 保持行长度为 80 个字符。
- 字符串优先使用双引号。
- 包名使用小写。
- message 名使用 CamelCase(首字母大写)。
- 字段名使用 underscore_separated_names。
- service 和 RPC 方法名使用 CamelCase。
- Unary RPC:客户端发送单个请求,服务端返回单个响应。
- Server Streaming:客户端发送请求,服务端返回消息流。
- Client Streaming:客户端发送消息流,服务端返回单个响应。
- Bidirectional Streaming:双方都发送消息流。
- 设计清晰、直观的服务接口。
- 把相关方法分组到同一个 service 中。
- 使用描述动作的有意义方法名。
- 用注释记录每个 RPC,说明行为、参数和返回值。
- 使用 gRPC 时复用 channel。
- 创建 gRPC channel 成本高,因为它会新建 HTTP/2 连接。
- 高吞吐场景实现连接池。
- 合理配置 keepalive 设置。
- 保持 message 大小合理——过大的 message 会影响性能。
- 大数据传输考虑使用 streaming。
- 在带宽受限环境使用压缩。
- 避免过深的嵌套 message 结构。
- 使用合适的 gRPC 状态码(OK、INVALID_ARGUMENT、NOT_FOUND 等)。
- 在 status details 中包含有意义的错误消息。
- 复杂错误场景使用丰富的错误详情。
- 在服务定义中记录预期错误条件。
- 对瞬时故障实现带指数退避的重试。
- 为所有 RPC 调用使用 deadline/timeout。
- 对 UNAVAILABLE 和 RESOURCE_EXHAUSTED 进行重试。
- 不要盲目重试非幂等操作。
- 生产环境使用 TLS 保障传输安全。
- 使用 metadata/headers 实现按 RPC 认证。
- 支持多种认证机制(JWT、OAuth2、mTLS)。
- 对每个请求验证凭据。
- 实现方法级访问控制。
- 使用拦截器集中实现授权逻辑。
- 无论认证状态如何,都验证所有输入数据。
- 遵循最小权限原则。
- 使用拦截器处理横切关注点(日志、认证、指标)。
- 仔细安排拦截器顺序——执行顺序很重要。
- 让拦截器专注于单一职责。
- 在拦截器内优雅处理错误。
- 添加 metadata(headers)用于追踪和认证。
- 实现请求/响应日志。
- 添加自动重试逻辑。
- 收集客户端指标。
- mock gRPC 服务以进行隔离测试。
- 测试 message 序列化/反序列化。
- 验证错误处理路径。
- 独立测试拦截器逻辑。
- 尽可能使用真实 gRPC 连接进行测试。
- 端到端验证 streaming 行为。
- 测试超时和取消场景。
- 用真实流量模式做负载测试。
- 使用 OpenTelemetry 进行跨服务边界的分布式追踪。
- 在 metadata 中传播 trace context。
- 同时对客户端和服务端埋点。
- 为每个 RPC 调用创建 span。
- 跟踪 RPC 延迟直方图。
- 按方法和状态码监控错误率。
- 统计活跃连接和 stream 数量。
- 对异常和 SLA 违规告警。
- 使用字段一致的结构化日志。
- 记录 RPC 方法、耗时和状态。
- 包含 trace ID 以便关联。
- 避免记录敏感数据。
- 使用官方 google.golang.org/grpc 包。
- 将服务实现为接口类型。
- 使用 context 进行取消和 deadline 控制。
- 利用 protoc-gen-go-grpc 进行代码生成。
- 使用 grpcio 和 grpcio-tools 包。
- 使用 grpcio-aio 实现异步服务以获得更好并发。
- 为生成的 stub 使用类型提示。
- 在异步上下文中正确处理阻塞调用。
- 使用 @grpc/grpc-js(纯 JavaScript 实现)。
- 考虑使用 nice-grpc 获得更好的 TypeScript 支持。
- 利用 async/await 模式。
- 使用静态代码生成以获得类型安全。
适用边界 · 注意事项
- 不适合不需要 schema/代码生成的简单 CRUD 或公开 REST/JSON API。
- 不适合浏览器直接调用且无法引入 gRPC-Web/Connect 等桥接方案的场景。
- 不适合团队无法维护 protobuf 编译器、插件和生成代码工具链的情况。