TL;DR
- 场景: 分布式服务拆分后,服务间通信从同步调用演进到异步解耦,伴随重试、幂等、可靠性问题
- 结论: 同步链路必须”有界重试+可观测+降级”,跨服务副作用用”任务化/MQ+幂等+补偿”兜底
- 产出: 一套选型对照(RPC vs HTTP)、同步/异步落地骨架、以及常见故障速查
分布式架构通信
SOA (Service-Oriented Architecture)
在面向服务的架构(SOA)中,系统设计会根据实际的业务需求进行模块化拆分。
SOA架构优势:
- 分布式特性:服务可以部署在不同的服务器或容器中
- 松耦合:服务之间通过接口契约进行交互
- 扩展灵活:可以根据业务需求单独扩展某个服务
- 可重用性:服务可以被多个业务系统复用
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 协议比较
| 特性 | RPC | HTTP |
|---|---|---|
| 协议 | TCP/UDP | TCP |
| 连接 | 长连接 | 短连接 |
| 序列化 | Protobuf/Thrift | JSON |
| 性能 | 更高 | 较低 |
| 调试 | 较难 | 容易 |
适用 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 刷新
分布式异步通信
消息队列优点
- 系统间解耦: 生产者和消费者不需要知道对方的存在
- 可恢复性: 当消费者暂时不可用时,消息会持久化在队列中
- 支持异构系统: 不同技术栈的系统可以通过统一的消息协议交互
- 并发处理能力: 可以启动多个消费者实例并行处理消息
- 流量削峰: 突发流量可以被消息队列缓冲
消息队列缺点
- 中间件瓶颈: 消息队列本身可能成为性能瓶颈
- 一致性性问题: 在分布式环境下难以保证严格的事务一致性
- 开发复杂性: 需要处理消息序列化、重试机制、死信队列等问题
- 运维成本: 需要维护消息中间件集群
错误速查
| 症状 | 根因 | 修复 |
|---|---|---|
| 同步重试导致 CPU 飙升/线程打满 | 无界重试、递归调用、缺少超时与退避 | 改为 for 有界重试;加入超时、退避、熔断 |
| 递归重试触发 StackOverflowError | 递归无退出条件 | 用迭代+最大次数;记录失败任务进入补偿队列 |
| 添加商品接口 RT 大、并发下吞吐下降 | 主链路同步做索引/静态页副作用 | 主链路只落库;副作用任务化 |
| 异步任务丢失 | 入队与落库非原子 | Outbox/事务消息:落库与任务记录同事务 |
| 任务重复执行导致重复刷新 | 至少一次投递 | 消费端幂等:业务唯一键/去重表 |
| MQ 顺序错乱导致状态回退 | 分区策略不一致、并行消费 | 以业务键分区;同 key 串行处理 |