已完成通信模式:同步、异步与事件驱动

约 32 分钟 · 阶段:分布式基石 · 能力:为 ShopFlow 选型同步/异步通信并理解编排与 Outbox

为什么要学本章

前四章定了边界与契约;本章回答服务之间怎么说话。选错模式会把 部分失败 放大成长链路阻塞,或把本该异步的通知硬拉成同步耦合。

学完你能
  • 为 ShopFlow 各集成点选择 REST/OpenFeign、gRPC 或消息,并说明理由
  • 对比编排与编舞,识别 Order 下单链适合哪种
  • 说明 幂等键、去重与 Outbox 如何配合异步事件
  • 将本章概念映射到 Spring Cloud 实战章节(OpenFeign、Kafka、Gateway)
本章先记住 3 件事
  • 同步要短:需要立即知道成败的步骤(预留库存)用请求/响应;链路过长须熔断、超时与幂等。
  • 异步解耦:支付结果、通知类用事件 + 消费者组;接受 最终一致,靠幂等与对账收敛。
  • 发消息要可靠:「写库 + 发 Kafka」用 Outbox,避免双写不一致。

请求/响应、RPC 与 gRPC 适用场景

请求/响应(Request/Response)是分布式中最直观的模式:调用方发请求、阻塞或异步等待响应。在 Java 微服务中常体现为 REST + OpenAPI 契约,practice 阶段用 OpenFeign 消费。

三种同步风格对比

风格特点ShopFlow 示例
REST/HTTP + JSON 通用、易调试、网关友好;性能略低于二进制 RPC order → inventory 预留、查询 SKU
RPC(如 gRPC) 强类型契约、HTTP/2、流式;需生成桩代码 高 QPS 内部调用、多语言团队(可选)
GraphQL / BFF 聚合 前端一次取多资源;聚合层负责扇出 商品详情页聚合(性能、容量与数据扩展 再展开)
同步滥用
  • order → inventory → payment → 通知 全同步链
  • 无超时,线程阻塞堆积
  • 下游慢一步拖垮整站
同步克制
  • 仅「须当场确认」的步骤同步(预留库存)
  • 显式超时 + 有限重试 + 幂等键
  • 支付结果改走事件(见下一节)

OpenFeign 调用预览(YAML)

yaml
# 详见 OpenFeign、负载均衡与服务间调用;此处仅示意超时与幂等头
spring:
  cloud:
    openfeign:
      client:
        config:
          inventory-service:
            connectTimeout: 1000
            readTimeout: 2000
# 调用时 Header: Idempotency-Key: {orderId}

消息队列、发布订阅与事件通知

当调用方不必立即知道下游处理结果,或需要削峰、解耦多订阅者时,使用异步消息。ShopFlow 中 PaymentCompleted、OrderCreated 等领域事件适合走 Kafka 等 MQ(详见 Kafka 事件驱动与 Redis 缓存)。

核心概念

  • 消息队列(Queue):点对点,一条消息通常被一个消费者处理(任务分发)。
  • 发布/订阅(Pub/Sub):一条事件可被多个订阅者各自处理(订单创建 → 库存、积分、通知各订一份)。
  • 领域事件:用过去式命名(OrderCreated、PaymentCompleted),载荷含最小集成字段 + 事件 ID。
  • 消费者组:同组内竞争消费保证扩展;不同组各自完整消费一遍。
ShopFlow · PaymentCompleted 发布订阅
flowchart LR
  Pay[payment-service] -->|publish| K[(Kafka topic)]
  K --> G1[order-service 消费者组]
  K --> G2[analytics 消费者组]
  G1 --> O[更新订单 PAID]
  G2 --> A[报表入库]

order 与 analytics 各用消费者组,互不影响进度;须幂等处理重复投递。

  • 须立即向用户反馈成败(预留库存、校验优惠券)
  • 链路短、调用方少、QPS 可控
  • 可接受 部分失败 语义已设计(幂等 + 查询)
  • 支付回调、发货通知、积分累计
  • 多下游订阅同一业务事实
  • 可接受秒级 最终一致

编排 vs 编舞(Choreography)

跨多个服务的业务流程(如下单 → 预留 → 支付 → 确认)如何协调?两种主流思路:编排(Orchestration) 由中央协调者按脚本推进;编舞(Choreography) 各服务监听事件、自行决定下一步,无中心大脑。

对比表

维度编排编舞
协调者 order-service 或独立 orchestrator 调用各步 各服务订阅事件,本地状态机驱动
可见性 流程集中在一点,易画序列图 流程分散,须链路追踪串联
耦合 协调者知悉所有步骤 API 服务只关心相关事件,添加订阅者较易
失败处理 常配合 Saga 补偿,由协调者触发 每服务自行补偿或发补偿事件
ShopFlow · 编排式下单(简化)
sequenceDiagram
  participant O as order-service 编排者
  participant I as inventory-service
  participant P as payment-service
  O->>I: 同步 reserve
  I-->>O: 201
  O->>P: 同步 initiate payment
  P-->>O: 202
  Note over O: 等待 PaymentCompleted 事件
  P-->>O: event PaymentCompleted
  O->>O: 状态 PAID

ShopFlow 入门采用「order 编排 + 支付异步事件」混合:关键同步、通知异步。

选型提示

步骤 ≤ 3 且团队小:order 内编排往往足够。步骤多、多团队自治:可逐步引入编舞,但须统一事件 schema 与可观测性。勿在 API 网关 里写编排逻辑。

幂等键、去重与 Outbox 模式预览

异步与重试使「至少一次投递」成为常态。须从生产端与消费端双侧保证正确性。

消费端:幂等与去重

  • 幂等键:同步写操作 Header Idempotency-Key;异步用 eventId 或业务键。
  • 去重表:消费者处理前插入 processed_events(event_id),重复则跳过。
  • 业务幂等:如「订单已 PAID 则忽略重复 PaymentCompleted」。

生产端:Transactional Outbox

问题:订单入库与发 Kafka 两个操作无法共用一个本地事务。 Outbox 将待发事件与业务写入同一数据库事务,后台投递器异步发到 MQ。

java
@Transactional
void createOrder(CreateOrder cmd) {
  Order order = orderRepo.save(new Order(cmd));
  outboxRepo.save(new OutboxEvent(
    "OrderCreated",
    order.getId().toString(),
    toJson(new OrderCreatedPayload(order.getId(), ...))
  ));
  // 同一事务;投递器稍后 poll outbox → Kafka
}
先想 10 秒:消费者收到两次相同 eventId 的 PaymentCompleted,应如何处理?

第二次应识别为重复:查去重表或订单状态已为 PAID,直接 ack 不再改库。若仍重复扣减或重复发券,说明缺少消费端幂等。

为 Spring Cloud 实战做概念映射

basics 阶段原理收官。下表将本章概念映射到 practice 阶段 ShopFlow 工程(Java 21 + Spring Boot 3.5 + Spring Cloud 2025)。

本章概念Spring Cloud / 组件实战章节
同步 REST + 契约 OpenFeign + LoadBalancer OpenFeign、负载均衡与服务间调用
统一入口、路由 Spring Cloud Gateway 服务注册发现与 API 网关
注册发现 Eureka / Nacos 服务注册发现与 API 网关
领域事件、Pub/Sub Spring Cloud Stream + Kafka Kafka 事件驱动与 Redis 缓存
Outbox、跨服务一致 本地事务 + 投递器 / Saga 分布式数据:事务、幂等与 Saga
脚手架、BOM Spring Boot 3.5 + Cloud BOM Spring Boot 3.5 与 Spring Cloud 脚手架
阶段一(basics)完成

你已具备分布式问题域、CAP、架构形态、DDD 边界、API 契约与通信选型基础。下一阶段在 ShopFlow 仓库中动手搭服务,建议从 Spring Boot 3.5 与 Spring Cloud 脚手架 开始。

本章结论

每条对应上一节 h3,basics 阶段收官复述。

  • 同步通信:REST/OpenFeign 适合须当场确认的步骤;链宜短,配超时、幂等与熔断;gRPC 等留给高性能内部场景。
  • 异步事件:Pub/Sub + 消费者组解耦支付通知等多订阅者;接受最终一致,消费端必须幂等。
  • 编排 vs 编舞:ShopFlow 入门用 order 编排 + 事件混合;编舞适合多团队自治但需统一事件契约与追踪。
  • 可靠投递:幂等键 + 去重表 + Outbox 解决重试与「写库发消息」原子性;与 CAP、BASE 与一致性模型 BASE 路径一致。
  • 实战映射:见上一节 Spring Cloud 映射表;下一阶段从 Spring Boot 3.5 与 Spring Cloud 脚手架 起步。

复习与自检

过关清单(勾选会保存在本浏览器)

0 / 5 已勾选

本章速查卡
概念一句
同步当场要结果;宜短链 + 幂等
异步事件解耦;消费幂等
编排中央协调多步
编舞各服务听事件自行反应
Outbox写库与待发事件同事务
答辩 / 面试口述题
  1. 为什么 PaymentCompleted 用 Kafka 而不是 order 同步轮询 payment?
  2. Outbox 与「先 commit 再发消息」有何区别?
  3. 编舞模式下如何排查「订单卡在未支付」?

下一步:勾选过关清单、完成测验与 Demo;进入 practice 阶段,从 Spring Boot 3.5 与 Spring Cloud 脚手架 搭建 ShopFlow 工程。

动手练习

填写同步/异步选型与 Spring Cloud 映射表,并解释 Outbox 与 createOrder 代码块关系。

操作步骤

  1. 复制「PaymentCompleted 发布订阅」Mermaid,在 order、analytics 消费者旁各写一句消费端幂等策略。
  2. 为「编排式下单」序列图中 reserve、initiate payment、等事件三步各标 sync/async。
  3. 对照 createOrder + OutboxEvent 片段:去掉 outbox 直接发 Kafka 会破坏哪条 BASE 配套手段?

判断练习

  1. 「order→inventory→payment→短信→积分」全 OpenFeign 同步且无超时。最先应改哪一处?

    参考答案

    支付改异步事件(或至少缩短链并设超时)。支付结果不必阻塞 order 线程;全同步长链最易触发「同步滥用」中的线程堆积与级联失败。

  2. PaymentCompleted 被 order 与 analytics 分别消费——编舞还是编排?新增 analytics 订阅者应改 order 还是只加消费者?

    参考答案

    analytics 侧属编舞(听事件自行处理)。新增 analytics 订阅者只加 Kafka 消费者,无需改 order 编排代码,这是编舞扩展性优势。

Demo:通信模式选型实验室

目录:demos/basics-05-communication-patterns-lab/。验收:集成点选型表 3 行均已填模式 + 理由。

延伸学习