TL;DR

  • 场景: 分布式服务拆分后,服务间通信从同步调用演进到异步解耦,伴随重试、幂等、可靠性问题
  • 结论: 同步链路必须”有界重试+可观测+降级”,跨服务副作用用”任务化/MQ+幂等+补偿”兜底
  • 产出: 一套选型对照(RPC vs HTTP)、同步/异步落地骨架、以及常见故障速查

分布式架构通信

SOA (Service-Oriented Architecture)

在面向服务的架构(SOA)中,系统设计会根据实际的业务需求进行模块化拆分。

SOA架构优势:

  1. 分布式特性:服务可以部署在不同的服务器或容器中
  2. 松耦合:服务之间通过接口契约进行交互
  3. 扩展灵活:可以根据业务需求单独扩展某个服务
  4. 可重用性:服务可以被多个业务系统复用

Dubbo + ZooKeeper 实现:

  • Dubbo作为高性能的RPC框架
  • ZooKeeper作为分布式协调服务,用于服务的注册与发现

微服务 (Microservices)

Feign 简介

Spring Cloud 中使用 Feign 来解决服务之间远程通信的问题。Feign 是一个轻量级的 RESTful HTTP 客户端。

Feign 的主要特点:

  • 声明式 REST 客户端
  • 集成了 Ribbon 实现客户端负载均衡
  • 支持 Hystrix 熔断机制
@FeignClient(name = "user-service")
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

RPC 与 HTTP 协议比较

特性RPCHTTP
协议TCP/UDPTCP
连接长连接短连接
序列化Protobuf/ThriftJSON
性能更高较低
调试较难容易

适用 RPC 的场景:

  • 系统间接口数量较多(>50个)
  • 高频交互需求(>1000次/分钟)
  • 对性能要求苛刻

适用 HTTP 的场景:

  • 系统间接口数量较少(<20个)
  • 交互频率较低(<100次/分钟)
  • 需要快速开发和调试

分布式同步通信

方法一:同步调用

Long goodsId = addGoods(goods);
refreshInvertedIndex(goods);
refreshStaticPage(goods);

带重试的同步方案:

private static final int MAX_RETRY = 3;

public Long saveGoods(Goods goods) {
    Long goodsId = addGoods(goods);
    boolean indexOk = retry(() -> indexService.refreshIndex(goods), MAX_RETRY, BASE_BACKOFF_MS);
    return goodsId;
}

方法二:异步任务

Long goodsId = addGoods(goods);
goodsTaskService.cache(goods); // 入队/缓存,后续由 worker 刷新

分布式异步通信

消息队列优点

  1. 系统间解耦: 生产者和消费者不需要知道对方的存在
  2. 可恢复性: 当消费者暂时不可用时,消息会持久化在队列中
  3. 支持异构系统: 不同技术栈的系统可以通过统一的消息协议交互
  4. 并发处理能力: 可以启动多个消费者实例并行处理消息
  5. 流量削峰: 突发流量可以被消息队列缓冲

消息队列缺点

  1. 中间件瓶颈: 消息队列本身可能成为性能瓶颈
  2. 一致性性问题: 在分布式环境下难以保证严格的事务一致性
  3. 开发复杂性: 需要处理消息序列化、重试机制、死信队列等问题
  4. 运维成本: 需要维护消息中间件集群

错误速查

症状根因修复
同步重试导致 CPU 飙升/线程打满无界重试、递归调用、缺少超时与退避改为 for 有界重试;加入超时、退避、熔断
递归重试触发 StackOverflowError递归无退出条件用迭代+最大次数;记录失败任务进入补偿队列
添加商品接口 RT 大、并发下吞吐下降主链路同步做索引/静态页副作用主链路只落库;副作用任务化
异步任务丢失入队与落库非原子Outbox/事务消息:落库与任务记录同事务
任务重复执行导致重复刷新至少一次投递消费端幂等:业务唯一键/去重表
MQ 顺序错乱导致状态回退分区策略不一致、并行消费以业务键分区;同 key 串行处理