一、微服务通信概述
微服务架构中,服务间通信是核心问题:
通信模式:
- 同步通信(REST、gRPC)
- 异步通信(消息队列、事件驱动)
- 编排模式(Orchestration)
- 协同模式(Choreography)
选型考虑因素:
- 业务复杂度
- 性能要求
- 一致性需求
- 可维护性
二、编排模式(Orchestration)
1. 核心概念
┌─────────────────────────────────────────────────────────────────┐
│ 编排模式 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────┐ │
│ │ 编排器(Orchestrator)│ │
│ │ 中心化控制 │ │
│ └───────────┬───────────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 服务A │ │ 服务B │ │ 服务C │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2. 同步编排实现
java
// 订单编排器
@Service
public class OrderOrchestrator {
@Autowired
private ProductService productService;
@Autowired
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;
@Autowired
private LogisticsService logisticsService;
@Transactional
public OrderResult createOrder(OrderRequest request) {
// 1. 验证商品
List<Product> products = productService.validateProducts(request.getItems());
// 2. 扣减库存
inventoryService.deductStock(request.getItems());
// 3. 创建订单
Order order = orderService.createOrder(request);
// 4. 处理支付
PaymentResult payment = paymentService.processPayment(order);
if (!payment.isSuccess()) {
// 支付失败,回滚库存
inventoryService.rollbackStock(request.getItems());
throw new PaymentException("支付失败");
}
// 5. 触发配送
logisticsService.createDelivery(order);
// 6. 返回结果
return OrderResult.builder()
.orderId(order.getId())
.paymentId(payment.getPaymentId())
.build();
}
}
3. 异步编排实现
java
// Saga编排器
@Service
public class OrderSagaOrchestrator {
@Autowired
private KafkaTemplate kafkaTemplate;
public String startOrderSaga(OrderRequest request) {
String sagaId = UUID.randomUUID().toString();
// 启动Saga
SagaState state = SagaState.builder()
.sagaId(sagaId)
.currentStep(0)
.status(SagaStatus.STARTED)
.request(request)
.build();
// 保存Saga状态
sagaStateStore.save(state);
// 发送第一个步骤
kafkaTemplate.send("saga-order", sagaId, new CreateOrderStep(sagaId, request));
return sagaId;
}
// 处理Saga步骤结果
public void handleStepResult(SagaStepResult result) {
SagaState state = sagaStateStore.findById(result.getSagaId());
if (result.isSuccess()) {
// 步骤成功,执行下一步
executeNextStep(state);
} else {
// 步骤失败,执行补偿
compensate(state, result.getStep());
}
}
private void executeNextStep(SagaState state) {
SagaStep nextStep = getNextStep(state);
if (nextStep == null) {
// Saga完成
state.setStatus(SagaStatus.COMPLETED);
sagaStateStore.save(state);
return;
}
state.setCurrentStep(nextStep.getStepNumber());
state.setStatus(SagaStatus.PROCESSING);
sagaStateStore.save(state);
// 发送步骤消息
kafkaTemplate.send("saga-order", state.getSagaId(), nextStep);
}
private void compensate(SagaState state, int failedStep) {
state.setStatus(SagaStatus.COMPENSATING);
sagaStateStore.save(state);
// 逆向执行已完成步骤的补偿操作
for (int i = failedStep - 1; i >= 0; i--) {
SagaStep step = getStep(i);
kafkaTemplate.send("saga-order-compensate",
state.getSagaId(), new CompensateCommand(step));
}
}
}
三、协同模式(Choreography)
1. 核心概念
┌─────────────────────────────────────────────────────────────────┐
│ 协同模式 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ 事件 ┌─────────┐ 事件 ┌─────────┐ │
│ │ 服务A │ ────────▶ │ 服务B │ ────────▶ │ 服务C │ │
│ │ │ ◀──────── │ │ ◀──────── │ │ │
│ └─────────┘ 命令 └─────────┘ 命令 └─────────┘ │
│ │
│ 服务自主决策,通过事件驱动协作 │
│ │
└─────────────────────────────────────────────────────────────────┘
2. 事件驱动实现
java
// 订单服务 - 发布事件
@Service
public class OrderService {
@Autowired
private KafkaTemplate kafkaTemplate;
public Order createOrder(OrderRequest request) {
// 创建订单
Order order = orderRepository.save(createOrder(request));
// 发布订单创建事件
kafkaTemplate.send("order-events", order.getId().toString(),
new OrderCreatedEvent(order));
return order;
}
}
// 库存服务 - 订阅事件
@Service
public class InventoryService {
@KafkaListener(topics = "order-events", groupId = "inventory-group")
public void handleOrderCreated(OrderCreatedEvent event) {
// 扣减库存
for (OrderItem item : event.getOrder().getItems()) {
deductStock(item.getProductId(), item.getQuantity());
}
// 发布库存扣减成功事件
kafkaTemplate.send("inventory-events",
event.getOrder().getId().toString(),
new InventoryReservedEvent(event.getOrder()));
}
}
// 支付服务 - 订阅事件
@Service
public class PaymentService {
@KafkaListener(topics = "inventory-events", groupId = "payment-group")
public void handleInventoryReserved(InventoryReservedEvent event) {
// 检查库存是否充足
if (event.isSuccess()) {
// 处理支付
PaymentResult result = processPayment(event.getOrder());
if (result.isSuccess()) {
// 发布支付成功事件
kafkaTemplate.send("payment-events",
event.getOrder().getId().toString(),
new PaymentSucceededEvent(event.getOrder(), result));
} else {
// 发布支付失败事件(触发库存回滚)
kafkaTemplate.send("payment-events",
event.getOrder().getId().toString(),
new PaymentFailedEvent(event.getOrder(), result.getReason()));
}
}
}
}
// 物流服务 - 订阅事件
@Service
public class LogisticsService {
@KafkaListener(topics = "payment-events", groupId = "logistics-group")
public void handlePaymentSucceeded(PaymentSucceededEvent event) {
// 创建物流
Delivery delivery = createDelivery(event.getOrder());
// 发布发货事件
kafkaTemplate.send("logistics-events",
event.getOrder().getId().toString(),
new OrderShippedEvent(event.getOrder(), delivery));
}
}
3. 协同模式的优势
java
// 服务自治
@Service
public class OrderService {
// 订单服务只需要关注订单相关的业务
// 不需要知道支付、库存、物流的具体实现
@KafkaListener(topics = "payment-events")
public void handlePaymentResult(PaymentEvent event) {
Order order = orderRepository.findById(event.getOrderId());
if (event.isSuccess()) {
order.setStatus(OrderStatus.PAID);
} else {
order.setStatus(OrderStatus.PAYMENT_FAILED);
order.setFailureReason(event.getReason());
}
orderRepository.save(order);
// 发布订单状态变更事件
kafkaTemplate.send("order-events", order.getId().toString(),
new OrderStatusChangedEvent(order));
}
}
四、编排vs协同对比
1. 对比表
| 维度 | 编排模式 | 协同模式 |
|---|---|---|
| 中心化 | 强中心化 | 去中心化 |
| 复杂度 | 编排器复杂 | 各服务简单 |
| 耦合度 | 编排器与服务耦合 | 服务间松耦合 |
| 可追踪性 | 好(编排器控制) | 差(需日志聚合) |
| 性能 | 好(同步调用) | 一般(异步消息) |
| 故障处理 | 编排器统一处理 | 各服务自行处理 |
| 适用场景 | 复杂业务流程 | 简单事件流 |
2. 选择决策
选择编排模式:
- 业务流程复杂,需要中心控制
- 需要强一致性
- 需要详细的执行追踪
- 团队习惯面向过程开发
选择协同模式:
- 业务流程简单,事件驱动
- 需要高可用和松耦合
- 团队习惯事件驱动开发
- 已有成熟的消息基础设施
五、混合架构
1. 组合使用
java
// 主流程编排,子流程协同
@Service
public class HybridOrderService {
@Autowired
private KafkaTemplate kafkaTemplate;
// 主流程:编排
public String createOrder(OrderRequest request) {
// 1. 编排器验证商品
List<Product> products = productService.validateProducts(request.getItems());
// 2. 编排器创建订单
Order order = createOrder(request);
// 3. 子流程:库存扣减(协同)
kafkaTemplate.send("inventory-commands",
order.getId().toString(),
new ReserveInventoryCommand(order.getItems()));
return order.getId().toString();
}
}
// 库存子流程:协同
@Service
public class InventoryService {
@KafkaListener(topics = "inventory-commands")
public void handleReserveCommand(ReserveInventoryCommand command) {
// 扣减库存
reserveStock(command.getItems());
// 发布库存预留事件
kafkaTemplate.send("inventory-events",
command.getOrderId(),
new InventoryReservedEvent(command.getOrderId(), true));
}
}
2. 事件溯源+协同
java
// 基于事件的Saga
public class OrderSaga {
@SagaStart
public void start(OrderRequest request) {
// 发送创建订单命令
send("order-service", new CreateOrderCommand(request));
}
@SagaEvent(topic = "order-created")
public void onOrderCreated(OrderCreatedEvent event) {
// 发送扣减库存命令
send("inventory-service", new ReserveStockCommand(event.getOrder()));
}
@SagaEvent(topic = "stock-reserved")
public void onStockReserved(StockReservedEvent event) {
// 发送支付命令
send("payment-service", new ProcessPaymentCommand(event.getOrder()));
}
@SagaEvent(topic = "payment-succeeded")
public void onPaymentSucceeded(PaymentSucceededEvent event) {
// 发送发货命令
send("logistics-service", new CreateDeliveryCommand(event.getOrder()));
// Saga完成
}
@SagaEvent(topic = "payment-failed")
public void onPaymentFailed(PaymentFailedEvent event) {
// 发送库存回滚命令
send("inventory-service", new ReleaseStockCommand(event.getOrder()));
// Saga补偿完成
}
}
六、异常处理
1. 编排模式异常处理
java
@Service
public class OrderOrchestratorWithCompensation {
@Transactional
public OrderResult createOrder(OrderRequest request) {
try {
// 1. 验证商品
List<Product> products = productService.validateProducts(request.getItems());
// 2. 扣减库存
inventoryService.deductStock(request.getItems());
// 3. 创建订单
Order order = orderService.createOrder(request);
// 4. 处理支付
PaymentResult payment = paymentService.processPayment(order);
if (!payment.isSuccess()) {
throw new PaymentException("支付失败");
}
return OrderResult.success(order);
} catch (PaymentException e) {
// 补偿:回滚库存
inventoryService.rollbackStock(request.getItems());
throw e;
} catch (Exception e) {
// 通用补偿逻辑
compensate(request);
throw e;
}
}
}
2. 协同模式异常处理
java
// 库存服务补偿逻辑
@Service
public class InventoryService {
@KafkaListener(topics = "payment-failed")
public void handlePaymentFailed(PaymentFailedEvent event) {
// 回滚库存
rollbackStock(event.getOrder().getItems());
// 发布库存已回滚事件
kafkaTemplate.send("inventory-events",
event.getOrder().getId().toString(),
new StockRolledBackEvent(event.getOrder()));
}
@KafkaListener(topics = "order-cancelled")
public void handleOrderCancelled(OrderCancelledEvent event) {
// 回滚库存
rollbackStock(event.getOrder().getItems());
}
}
七、最佳实践
1. 通信协议选择
java
// 同步通信:REST/gRPC
@RestController
public class ProductController {
@GetMapping("/products/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.getProduct(id);
}
}
// 异步通信:消息队列
@Service
public class OrderEventPublisher {
@Autowired
private KafkaTemplate kafkaTemplate;
public void publishOrderCreated(Order order) {
kafkaTemplate.send("order-events", order.getId().toString(),
new OrderCreatedEvent(order));
}
}
2. 服务间契约
java
// 定义契约
@Contract
public interface OrderServiceContract {
@Endpoint("http://order-service/api")
interface OrderApi {
@Post("/orders")
@RequestBody(OrderRequest.class)
@ResponseBody(OrderResponse.class)
OrderResponse createOrder(OrderRequest request);
@Get("/orders/{id}")
@ResponseBody(Order.class)
Order getOrder(@PathVariable Long id);
}
}
3. 监控和追踪
java
// 分布式追踪
@Component
public class TracingInterceptor {
@Around("@annotation(org.springframework.web.bind.annotation.*)")
public Object trace(ProceedingJoinPoint point) throws Throwable {
String traceId = getTraceId();
String spanId = generateSpanId();
try {
Object result = point.proceed();
// 记录追踪信息
tracingService.record(traceId, spanId,
point.getSignature().getName(), true, null);
return result;
} catch (Exception e) {
tracingService.record(traceId, spanId,
point.getSignature().getName(), false, e.getMessage());
throw e;
}
}
}
八、总结
微服务通信架构选择:
- 编排模式:适合复杂业务流程,强中心控制
- 协同模式:适合简单事件流,去中心化
- 混合模式:主流程编排,子流程协同
- Saga:长流程事务处理
最佳实践:
- 根据业务复杂度选择模式
- 设计好服务间契约
- 做好异常处理和补偿
- 实现完善的监控追踪
个人观点,仅供参考