3. 服务通信基础与协议选型
微服务拆开之后,服务之间“怎么说话”就成了架构里最先要定下来的事。协议选型不是追求最新最酷,而是让每一次调用的语义、时延、吞吐、可观测性和运维成本都落在可接受的范围内。在 Google Cloud 上,常见的四种通信方式各有清晰的位置:REST 适合对外与通用集成,gRPC 适合内部高频强类型调用,HTTP/2 是承载 gRPC 与多路复用的传输底座,WebSocket 适合服务端主动推送的双向长连接。理解它们的差异,比记住“哪个更快”更重要。
四种协议的本质差异
REST 通常指基于 HTTP/1.1 或 HTTP/2 的 JSON over HTTP,用 URL 表达资源、用方法表达动作,语义直观、调试方便,浏览器和任何语言都能直接消费。它的代价是文本序列化开销大、每次请求都要携带较多元数据,且 HTTP/1.1 下同一连接难以真正并行。
gRPC 建立在 HTTP/2 之上,用 Protocol Buffers 定义服务契约并做二进制序列化。它天然支持四种调用模式:一元调用、服务端流、客户端流、双向流。强类型契约让跨语言代码生成和向后兼容更可控,二进制编码让报文更小、解析更快,HTTP/2 的多路复用让一个连接上可以并发多个请求,避免了 HTTP/1.1 的队头阻塞。
HTTP/2 本身不是一种“业务协议”,而是传输层能力:二进制分帧、多路复用、头部压缩(HPACK)、服务端推送和流优先级。gRPC 正是把这些能力用满的典型。理解 HTTP/2 有助于判断:当内部调用量大、连接数多时,多路复用能显著减少连接建立与 TLS 握手的开销。
WebSocket 通过一次 HTTP 升级握手建立全双工长连接,之后双方可以随时互发消息。它适合聊天、协同编辑、实时行情、任务进度推送等“服务端需要主动通知客户端”的场景。代价是连接需要保活、状态需要管理,且不像 REST 那样天然无状态、易水平扩展。
在 Google Cloud 上如何落地选型
对外暴露的 API 优先用 REST。Cloud Endpoints 和 Apigee 对 OpenAPI 的支持成熟,鉴权、配额、审计都能直接挂上去;浏览器和第三方系统无需额外 SDK 即可调用。如果外部客户端确实需要流式或高频交互,可以在网关层做 gRPC-Web 或 JSON 转码,把外部 REST 请求翻译成内部 gRPC 调用,而不是让外部直接说 gRPC。
内部服务之间优先用 gRPC。Cloud Run 和 GKE 都原生支持 HTTP/2,gRPC 服务可以正常做健康检查和负载均衡。用 Protocol Buffers 定义 service 和 message,通过 protoc 生成各语言客户端,契约即文档。一个典型的一元调用定义如下:
service OrderService {
rpc GetOrder (GetOrderRequest) returns (Order);
rpc WatchOrders (WatchRequest) returns (stream OrderEvent);
}
message GetOrderRequest {
string order_id = 1;
}
message Order {
string order_id = 1;
string status = 2;
int64 amount_cents = 3;
}
当需要服务端持续推送订单状态变化时,用 stream OrderEvent 的服务端流,比客户端轮询 REST 接口更省资源、时延更低。双向流则适合需要客户端和服务端持续对话的场景,例如实时协作或长任务进度回传。
WebSocket 在 Google Cloud 上通常不直接暴露给公网,而是放在 Cloud Run 或 GKE 的 WebSocket 网关后面,配合 Pub/Sub 做扇出。客户端与网关建立长连接,网关订阅 Pub/Sub 主题,把事件推给对应连接。这样业务服务仍然是无状态的,推送的连接状态集中在网关层,扩容和故障恢复都更简单。
性能权衡与常见误区
gRPC 的二进制编码和 HTTP/2 多路复用确实能降低单次调用的网络开销,但这不意味着所有内部调用都必须换成 gRPC。如果调用频率很低、报文很小,REST 的可读性和调试便利性反而更有价值。真正需要关注的是高频、大报文、强类型契约和流式这几类场景。
另一个常见误区是把 HTTP/2 当成“打开就更快”。HTTP/2 的多路复用需要 TLS 和代理支持,如果中间有只支持 HTTP/1.1 的负载均衡或旧版代理,连接会被降级,多路复用失效。在 GKE 上使用 gRPC 时,要确认 Service 的负载均衡策略和 Ingress 支持 HTTP/2,否则可能出现连接复用异常或请求分布不均。
WebSocket 的代价常被低估。长连接意味着服务端要维护连接状态、处理心跳和断线重连,连接数上限和内存占用都需要容量规划。如果只是“服务端偶尔通知一下”,用 Pub/Sub 加客户端轮询或 Server-Sent Events 可能更简单;只有真正需要低时延双向交互时,WebSocket 才是合适选择。
| 判断维度 | REST | gRPC | WebSocket |
|---|---|---|---|
| 调用方向 | 客户端请求 / 服务端响应 | 请求响应 + 双向流 | 全双工 |
| 契约形式 | OpenAPI / JSON | Protobuf IDL | 自定义消息格式 |
| 浏览器直连 | 原生支持 | 需 gRPC-Web | 原生支持 |
| 典型 GCP 组件 | Cloud Endpoints、Apigee | Cloud Run、GKE、Traffic Director | Cloud Run、Pub/Sub、GKE 网关 |
实际项目中,三种协议往往共存:外部 REST 入口、内部 gRPC 调用、实时 WebSocket 推送。关键是让每种协议出现在它最合适的位置,而不是用一种协议硬套所有场景。选型确定后,把契约、超时、重试和可观测性一起设计进去,通信层才不会成为微服务治理的短板。