摘要:同步通信是服务间最直觉的通信方式:A 调 B,等 B 给结果再往下走。本文讲清三个关键决策——什么时候必须同步、REST 和 gRPC 各自适合谁、同步链路上防"层层超时"的取舍。用商城一笔下单串起整条链路。
用户点"下单",订单服务要立刻知道"库存够不够""支付能不能划账",才能给用户一个明确的号。这种"我等着要结果"的场景,就是同步通信的地盘。同步很简单:A 发起请求,B 处理,A 拿到响应才继续。但简单是错觉——在微服务里,一次同步往往不是一对一,而是一长串"逐级等待"。
先看它长什么样:
订单服务在这一串里是"中间人",两头都等。它自己的延迟 = 库存延迟 + 支付延迟 + 网络抖动。这就是同步通信最重要的隐藏成本:延迟像账单一样逐级累加。你问"为什么订单一单要一秒",很可能就是链路里最慢的一环拖住了所有人。
三个信号会逼你走同步:
如果这三条都不满足,那它大概率可以走异步——下一节讲。
同步通信最常见的两个载体是 REST(对着 HTTP,自描述、人类可读)和 gRPC(对着 proto 契约,二进制、高性能)。
| 维度 | REST(HTTP/JSON) | gRPC(HTTP/2 + Protobuf) |
|---|---|---|
| 接口怎么看 | 一个 URL + 动词,curl 就能调 | 需要先有 proto 契约 |
| 性能 | 一般,JSON 序列化偏重 | 好,二进制更快带宽更省 |
| 生态与招人 | 非常普及,人人会 | 需懂 protobuf,工具相对小众 |
| 适合 | 对外 API、浏览器、跨语言边界的便利店式调用 | 服务间高吞吐、低延迟的内部调用 |
拿商城举例:对外(订单服务暴露给网关、客户端的接口)走 REST,因为它要能被手机、浏览器、第三方轻松调用,可读性重要。对内(订单与库存、支付这类高并发服务对服务)可以上 gRPC,因为它们在同一个机房、流量大、要求低延迟。一句话:对外的路要宽要平(REST),对内的路要快要稳(gRPC)。
同步链路最怕的是"永远等下去"。A 调 B,B 卡住,A 的线程被占住不放;层层如此,线程池被拖垮,整站雪崩。所以每个同步调用必须有显式超时。下面是一个最小示意(伪代码表达意图,不算可运行实现):
try: result = stock_client.check_and_deduct(order, timeout=2.0) except TimeoutError: # 超时不是"成功",也不是"失败",是需要策略的第三个状态 logger.warning("库存扣减超时,进入补偿流程") compensation_queue.put(order)
这里的重点是第三个状态:超时 ≠ 失败。B 可能其实成功了,只是响应丢了。所以超时后的处理要特别小心,很多重复扣库存、重复发短信的线上事故,就是没想清楚"超时到底是成功了还是失败了",本章先埋个伏笔,重试和补偿我们到第 6 章、第 4 章各自展开。
把整条链路全部同步是最省事的,也是最危险的——任何一环抖动都会传递到用户。更稳的做法是把"必须立刻结论"的一小段同步,把"可以缓一缓"的大段切异步。比如支付回调这种环节,完全可以"先收单,支付结果异步通知",用户看到的是"支付处理中"。这个"能异步就别同步"的倾向,会显著减小延迟金字塔。
最常见的错用,是某个操作明明不急着要结果(比如"下单后发一封确认邮件"),却在主链路里同步调用邮件服务,用户下单硬生生多等一秒钟,还把邮件服务的故障拖进了下单的成功率里。识别方法很简单:下一秒没有它,这笔交易还成立吗? 答案是不成立就同步,成立就异步。
既然同步的延迟会逐级累加,那就要有人为这条链设一个"顶"。做法是延迟预算(latency budget):先定"这一个入口最多能给用户多少毫秒",再往下把预算分给每一跳。比如网关目标 200ms,就拆成网关→订单 80ms、订单→库存 50ms、订单→支付 50ms、预留 20ms 缓冲,每跳各配各的超时,谁超时谁先兜。
这一步的价值是倒逼取舍:当某一段拿不到足够的预算,你就会有据可依地说"这里不能做同步了,改异步",而不是上线后任由最慢一环把整条链拖到爆。没有预算的同步链路,大家各自设个"宽松的超时意思一下",结果就是"每一环都觉得没事,合起来对用户是灾难"。给链路一个能守住的上限,是同步通信里最容易被省掉、却最能救命的一步。
同步链路还有一个容易内伤的点:调用方习惯在出错时"再试一次试试"。对单个请求,这是不多的一次开销;但在一串同步调用上,每一跳的无脑重试会呈几何级放大——最内层重试三次,中间层再补一刀,最外层可能就白白多等了一个超时时长,延迟预算瞬间失守。更糟的是,当整条链进入半故障状态、人人都在重试时,活下来的那部分请求也要陪着一起慢下来。同步通信里的重试要克制:要么只在明确值得重试的幂等操作上做,要么交给第 6 章的熔断与舱壁去编排,而不是让每个调用方自己碰运气重试。等不起了就走降级,别抱住"多试一次说不定就成了"的侥幸。
一句话回顾:同步通信不是工具选的,是业务"多久要结果"推出来的
同步=逐级等待,延迟逐级累加,好用但要防链路
用户盯着、要原子决策、结果决定下一步这三个信号才值得同步
对外用 REST 图可读生态,对内用 gRPC 图性能
每个同步调用都要有显式超时,且记住"超时≠失败"
能异步就不同步,别把不急着要结果的动作拖进主链路