4. 微服务通信基础与协议选择

微服务拆开之后,服务之间怎么“说话”就成了架构成败的关键。通信方式选错,往往不是性能差一点,而是把整个系统拖进耦合、超时、雪崩的泥潭。本节围绕同步与异步两条主线,梳理 REST/HTTP、gRPC、GraphQL 以及消息队列与事件驱动在 OCI(Oracle Cloud Infrastructure)中的适用场景,并给出可落地的判断依据。

同步与异步:先分清“等不等结果”

同步通信指调用方发出请求后阻塞等待响应,典型如 REST 和 gRPC。它的优点是语义直观、链路清晰、便于调试;代价是调用方与被调方在时间上强绑定,被调方变慢或不可用会直接传导为调用方超时。异步通信指调用方把消息投递到中间件后立即返回,由消费方在稍后处理,典型如 OCI Streaming、OCI Queue。它解耦了时间与可用性,但引入了最终一致性、消息重复与顺序问题。

判断标准可以归纳为三点:是否需要立即拿到结果——查询类、校验类通常同步;是否能容忍最终一致——通知、审计、积分累计通常异步;调用方是否必须知道被调方存在——若不应知道,则应走事件而非直接调用。

微服务通信方式选型对照 同步通信(请求—响应) 调用方服务 阻塞等待响应 被调方服务 处理并返回 请求 响应 REST / HTTP 资源化 URL、JSON、状态码语义 适合:对外 API、CRUD、浏览器直连 OCI:API Gateway + 负载均衡 弱项:文本开销大、无强类型契约 gRPC HTTP/2 + Protobuf,双向流、强类型 适合:内部高频调用、低延迟链路 OCI:OKE 内 Pod 间通信、服务网格 弱项:浏览器支持弱、调试门槛高 异步通信(消息 / 事件驱动) 生产者 投递即返回 消息中间件 缓冲 / 持久化 / 重投 消息队列(点对点) 一条消息仅被一个消费者处理 适合:任务分发、削峰填谷、重试 OCI:OCI Queue(托管队列) 关注:可见性超时、死信队列 事件驱动(发布 / 订阅) 一个事件被多个订阅方各自消费 适合:状态广播、跨域解耦、审计 OCI:Events + Streaming + Functions 关注:幂等、顺序、重复消费

REST/HTTP:对外契约的首选

REST 以资源为中心,用 HTTP 方法表达动作、用状态码表达结果,天然适配浏览器、移动端和第三方集成。在 OCI 中,典型落地方式是 API Gateway 暴露统一入口,后端由 OKE 中的服务或 Functions 承载,网关负责鉴权、限流、路由与 TLS 终止。示例:订单查询接口 GET /orders/{id},网关配置 1000 请求/秒的限流,后端返回 200 与 JSON 体;若订单不存在返回 404,参数非法返回 400。这些状态码本身就是契约的一部分,客户端据此决定重试还是提示用户。

REST 的短板在于:每次调用携带完整文本头,序列化开销大;接口演进依赖版本号(如 /v2/orders),容易产生版本碎片;没有强类型契约,字段变更靠文档约束。因此它更适合“对外、低频、可读性优先”的场景,而不是内部高频调用。

gRPC:内部高频链路的利器

gRPC 基于 HTTP/2 多路复用与 Protobuf 二进制编码,天然支持一元调用、服务端流、客户端流和双向流。它的接口由 .proto 文件定义,编译期即校验字段类型,契约强、体积小、延迟低。在 OKE 内部,订单服务调用库存服务扣减库存,若 QPS 达到数千且要求 P99 在 20ms 内,gRPC 通常比 REST 更合适。

service Inventory {
  rpc Deduct(DeductRequest) returns (DeductReply);
}
message DeductRequest {
  string sku = 1;
  int32  qty = 2;
  string orderId = 3;
}

需要注意的是,gRPC 依赖 HTTP/2,浏览器直连支持有限,通常需要 gRPC-Web 或网关转换;同时 Protobuf 的字段编号一旦发布不可随意更改,删除字段应保留编号并标记 reserved,否则会造成新旧版本解析错乱。

GraphQL:聚合查询的折中方案

GraphQL 让客户端声明“我要哪些字段”,由服务端一次返回,避免 REST 的多次往返与字段冗余。它适合 BFF(Backend for Frontend)层:移动端首页需要用户、订单、优惠券三类数据,若分别调用三个 REST 接口需三次往返,而 GraphQL 一次查询即可聚合。在 OCI 中,GraphQL 服务通常部署在 OKE,后端再以 gRPC 或 REST 调用各领域服务。

但 GraphQL 也带来新问题:查询复杂度不可控,恶意深嵌套可能拖垮后端,需要设置深度限制与查询成本上限;缓存比 REST 的 URL 缓存更复杂;错误处理返回 200 加 errors 数组,监控需专门适配。因此它更适合“客户端多样、聚合需求强”的场景,而非所有接口一律 GraphQL 化。

消息队列与事件驱动:解耦与削峰

当调用方不需要立即结果时,应转向异步。OCI Queue 适合点对点任务分发:订单创建后投递一条“生成发票”消息,发票服务消费并处理,失败则重投,超过次数进入死信队列人工排查。OCI Streaming 适合发布/订阅与事件流:订单状态变更事件被通知服务、积分服务、数据仓库各自订阅,互不影响。

异步通信必须处理三个工程问题:幂等——消息可能重复投递,消费方需以业务唯一键(如 orderId)去重;顺序——同一订单的事件应保证有序,可用分区键将同一订单路由到同一分区;可观测——消息积压、消费延迟、死信数量需纳入监控,OCI 控制台与 Metrics 可观察队列深度与消费速率。

选型口诀:对外可读用 REST,内部高频用 gRPC,聚合查询用 GraphQL,解耦削峰用消息,广播通知用事件。没有万能协议,只有与场景匹配的协议。

选型决策要点

维度REST/HTTPgRPCGraphQL消息/事件
通信模式同步同步/流同步异步
契约强度弱(文档)强(proto)中(Schema)中(消息格式)
典型延迟中低中不适用
OCI 承载API GatewayOKE/服务网格OKE + BFFQueue/Streaming
主要风险版本碎片浏览器兼容查询复杂度重复与顺序

实际系统中,这四种方式往往并存:对外 REST 暴露 API,内部 gRPC 承载核心链路,BFF 用 GraphQL 聚合,跨域状态变更走事件。关键不是选一个“最好”的协议,而是让每条通信路径都与其一致性要求、延迟预算和耦合容忍度相匹配。