事件驱动架构:异步解耦的最佳实践

事件驱动架构:异步解耦的最佳实践

事件驱动架构是微服务解耦的核心方案。本文从原理到实战,带你掌握事件驱动设计。


一、什么是事件驱动?

1.1 传统调用 vs 事件驱动

scss 复制代码
传统调用(同步):
OrderService.create() → 调用 InventoryService.deduct() → 调用 PaymentService.pay()
                    ↓ 失败
             全部回滚

事件驱动(异步):
OrderService.create() → 发送 OrderCreatedEvent
  ↓
InventoryService 监听 → 扣减库存 → 发送 StockDeductedEvent
  ↓
PaymentService 监听 → 处理支付

1.2 核心组件

组件 作用
Event 事件对象
Producer 事件生产者
Consumer 事件消费者
Event Bus 事件总线(MQ)

二、事件驱动优势

复制代码
✅ 服务解耦 - 服务间不直接调用
✅ 弹性伸缩 - 消费者独立扩展
✅ 最终一致 - 保证数据最终一致
✅ 流量削峰 - MQ 缓冲流量

三、实战代码

3.1 定义事件

kotlin 复制代码
@Data
public class OrderCreatedEvent {
    private String orderId;
    private String userId;
    private BigDecimal amount;
    private List<OrderItem> items;
    private Long timestamp;
}

3.2 发送事件

scss 复制代码
@Service
public class OrderService {
    
    @Autowired
    private ApplicationEventPublisher publisher;
    
    @Autowired
    private RocketMQTemplate mqTemplate;
    
    public void createOrder(OrderRequest request) {
        // 1. 创建订单
        Order order = orderMapper.insert(request);
        
        // 2. 发送事件
        OrderCreatedEvent event = OrderCreatedEvent.builder()
            .orderId(order.getId())
            .userId(order.getUserId())
            .amount(order.getAmount())
            .items(order.getItems())
            .timestamp(System.currentTimeMillis())
            .build();
        
        mqTemplate.asyncSend("order-created", event, new SendCallback() {
            @Override
            public void onSuccess(SendResult result) {
                log.info("事件发送成功");
            }
            @Override
            public void onException(Throwable e) {
                log.error("事件发送失败", e);
            }
        });
    }
}

3.3 消费事件

less 复制代码
@Service
@RocketMQMessageListener(
    topic = "order-created",
    consumerGroup = "inventory-consumer"
)
public class InventoryConsumer implements RocketMQListener<OrderCreatedEvent> {
    
    @Autowired
    private InventoryService inventoryService;
    
    @Override
    public void onMessage(OrderCreatedEvent event) {
        try {
            // 幂等性检查
            if (processed(event.getOrderId())) {
                return;
            }
            
            // 扣减库存
            for (OrderItem item : event.getItems()) {
                inventoryService.deduct(item.getProductId(), item.getQuantity());
            }
            
            // 发送下一个事件
            eventPublisher.publishEvent(new StockDeductedEvent(event));
            
        } catch (Exception e) {
            log.error("库存扣减失败", e);
            // 进入死信队列
        }
    }
}

四、常见问题

4.1 幂等性

typescript 复制代码
// 使用 Redis 去重
public boolean isDuplicate(String eventId) {
    return !redisTemplate.opsForValue()
        .setIfAbsent("event:" + eventId, "1", 24, TimeUnit.HOURS);
}

4.2 顺序性

arduino 复制代码
// 使用消息队列的分区
// 相同 orderId 的消息发到同一个分区
rocketMQTemplate.syncSendOrderly(
    "order-topic",
    message,
    orderId  // 分区键
);

4.3 事务性

typescript 复制代码
// 事务消息
@Transactional
public void createOrder(OrderRequest request) {
    Order order = orderMapper.insert(request);
    
    // 事务消息:本地事务成功才发送
    TransactionMQProducer producer = new TransactionMQProducer();
    producer.sendMessageInTransaction(
        new Message("order-topic", JSON.toJSONBytes(order)),
        new LocalTransactionExecuter() {
            @Override
            public LocalTransactionState executeLocalTransactionBranch(Message msg, Object arg) {
                // 执行业务逻辑
                return LocalTransactionState.COMMIT_MESSAGE;
            }
        }
    );
}

五、架构模式

5.1 事件溯源(Event Sourcing)

csharp 复制代码
// 不存储状态,只存储事件
@Entity
public class OrderAggregate {
    private String orderId;
    private List<OrderEvent> events = new ArrayList<>();
    
    public void apply(OrderCreatedEvent event) {
        events.add(event);
        // 重放所有事件得到当前状态
    }
}

5.2 CQRS

less 复制代码
// 读写分离
@Service
public class OrderQueryService {
    // 读:从 Elasticsearch/Redis 查询
}

@Service
public class OrderCommandService {
    // 写:生成事件,写入 Event Store
}

六、技术选型

MQ 特点 适用场景
Kafka 高吞吐、持久化 日志、大数据
RabbitMQ 路由灵活 企业集成
RocketMQ 事务消息 电商金融
Pulsar 云原生 多租户

相关推荐
高频因子挖掘机42 分钟前
量化项目的数据架构怎么设计?从第一份行情到可扩展的 Python 研究环境
后端·github·api
考虑考虑8 小时前
JDK26中的Map.ofLazy
java·后端·java ee
云浪10 小时前
Go 并发实战:从零实现一个高并发安全的任务调度器
javascript·后端·go
苏三的开发日记10 小时前
Docker 启动顺序优化(CentOS / Ubuntu)
后端
高频因子挖掘机12 小时前
同一交易日出现重复 K 线:去重前先确认时间、来源和粒度
后端·github·api
feely020813 小时前
《我写了 31 个静态扫描插件,踩了 7 个「假绿」》
后端
橙子家13 小时前
参数校验类异常(Argument Exception)【C# Exception 详解系列一】
后端
掘金挖土14 小时前
前端手摸手跑路之 AI 应用开发(九)
前端·后端
全栈弄潮儿14 小时前
Python实战第8期:函数
后端·python·编程语言