自己做微服务也有七八年了,这期间踩过最深的坑基本都集中在服务间通信上。有一次线上压测,一个下游接口从 80ms 慢到 800ms,结果上游线程池直接被打满,紧接着整条调用链路都开始连环报错。后来定位下来,问题根本不在业务代码里,而是通信策略没设计好:超时设得过于宽松、重试没有上限、熔断和隔离都没接。那次事故之后我就养成了一个习惯——每引入一种新的通信方式,都要先把“它靠不靠谱”这件事聊清楚。
如果你正在搭微服务,或者已经被线上偶发的超时、重试风暴、消息堆积折磨过,这篇文章应该能帮你少走不少弯路。它不是讲某个具体框架的 API 怎么用,而是把微服务架构中服务间通信的策略从选型、可靠性到数据一致性串起来,梳理成一套可以直接落地的思路。
1. 先想明白:你的服务之间到底要“怎么说话”
1.1 同步与异步,是第一个岔路口
服务间通信最底层的分岔路口,就是同步和异步。
同步调用简单直接:服务 A 发请求给服务 B,B 处理完返回结果,A 继续往下走。登录、下单、余额查询这类需要“即时拿到结果”的场景,用同步是自然的。同步的问题也隐藏在“简单”里——只要下游有一丁点抖动,上游的线程就跟着一起等。接口超时设置不合理时,1000 个并发就能把线程池堆满,雪崩的起点往往就这么来的。
异步调用则是一方发完消息就去做自己的事,另一方什么时候处理、处理得怎么样,调用方不关心。发短信、发邮件、异步对账这类场景,天然适合异步。但异步会引入额外的复杂度:消息丢了怎么办、重复投递怎么幂等、消费者发生故障后积压怎么处理。很多团队一到这一步就开始犹豫,于是选了同步,结果把系统压垮了。
我的判断标准很简单:调用方是不是真的需要强实时结果?如果业务允许“过一会儿再知道结果”,就不要同步等着。如果必须同步拿结果,也尽量用异步方式去支撑内部的耗时逻辑,比如先快速锁定订单、再异步算价格和库存,而不是所有环节都同步调用一遍。
1.2 先画一遍业务链路,分清楚强依赖和弱依赖
很多团队选通信方式时,习惯先选一个框架,再往项目里硬塞,这是本末倒置。正确姿势是先画出核心业务链路上每一步调用,标清楚哪些是强依赖、哪些是弱依赖。
强依赖意味着“没有这个调用,主流程就进行不下去”。比如下单时必须扣减库存,这就是强依赖。对强依赖的服务,你要做的是提升它的可用性、容量和降级预案,而不是把它改造掉。弱依赖则意味着“就算挂了,也能通过其他方式补偿或稍后恢复”。比如下单后的会员积分累计,通常不需要同步完成,晚几分钟也不影响主流程。对弱依赖,优先考虑异步化、消息解耦,甚至失败后直接默默重试,不要让它拖住主线。
把依赖关系梳理清楚后,通信策略的组合自然就出来了:强同步依赖要加熔断、限流、重试和可观测性;弱依赖尽量放进消息队列或定时补偿任务里。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主流通信策略选型:REST、gRPC、消息队列与事件驱动
2.1 REST/HTTP:最简单,但也很容易被滥用
REST 是大家最熟悉的服务间通信方式,优点是门槛低、生态好、跨语言方便,测试工具链也最齐全。很多团队从单体拆分出来后,最习惯的写法就是继续用 Spring MVC 调 Remote 接口。
REST 的问题在服务数量多起来后开始暴露:第一,接口契约是弱约束,改字段时经常不知不觉破坏下游兼容性;第二,JSON 序列化和 HTTP 头解析在高并发、大流量下性能不如二进制协议;第三,HTTP 状态码往往被当成业务码来用,导致错误语义不统一。我见过一个系统,上游把业务异常也塞进 HTTP 状态码里,下游状态码判断直接写乱码情况严重的部分,出错排查很难。
因此我的建议是:对外部系统,或者团队边界外的接口,用 REST 问题不大;但对内部核心链路的频繁调用,REST 可以作为兜底通信方式,最好还是配合显式的 API 版本、DTO 校验和契约测试。契约测试很重要,它能在不真正跑起整套环境的前提下,提前发现接口定义不兼容的问题,比联调时抓瞎高效很多。
2.2 gRPC:高性能与强契约下的更优解
如果服务 A 和服务 B 之间是高频、对性能敏感的内部调用,我会优先考虑 gRPC。它的核心优势有两个:一是基于 Protobuf 的强类型接口定义,接口变更可以被编译器拦住,而不是等运行时才炸;二是基于 HTTP/2 多路复用和二进制传输,同流量下延迟和带宽占用通常比 REST 有优势。
gRPC 也不是没有代价。它的调试工具没有 REST 那么直观,浏览器直接访问不方便,很多团队还得额外搭 grpc-web 代理;边界网关、Spring Cloud G
