Spring Boot 轻量级分布式事务:基于消息最终一致性的实战

117 阅读5分钟

一、分布式事务困境:ACID vs BASE

1.1 传统方案的局限性

image.png

1.2 轻量级方案核心思想

image.png

核心原则:

  • 最终一致性:允许短暂不一致
  • 事件驱动:通过消息解耦服务
  • 幂等设计:支持重复消费
  • 补偿机制:失败自动重试

二、Spring Boot实现方案:事务消息+本地事件表

2.1 架构设计

image.png

2.2 核心依赖

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>


    <!-- RocketMQ -->
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.2.3</version>
    </dependency>


    <!-- MyBatis Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>


    <!-- 分布式ID生成 -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.16</version>
    </dependency>
</dependencies>

三、核心实现源码

3.1 事件表设计

@Data
@TableName("distributed_event")
public class DistributedEvent {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String eventType;   // 事件类型:ORDER_CREATED, PAYMENT_SUCCESS
    private String payload;     // JSON格式事件数据
    private String status;      // 状态:NEW, PROCESSING, SUCCESS, FAILED
    private Integer retryCount; // 重试次数
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}


// 事件状态枚举
public enum EventStatus {
    NEW, PROCESSING, SUCCESS, FAILED
}

3.2 本地事务管理器

@Service
@Transactional
public class TransactionCoordinator {


    private final DistributedEventMapper eventMapper;
    private final RocketMQTemplate rocketMQTemplate;


    public void executeInTransaction(Runnable businessLogic, String eventType, Object payload) {
        // 1. 执行业务逻辑
        businessLogic.run();


        // 2. 保存事件到数据库
        DistributedEvent event = new DistributedEvent();
        event.setEventType(eventType);
        event.setPayload(JSON.toJSONString(payload));
        event.setStatus(EventStatus.NEW.name());
        event.setRetryCount(0);
        eventMapper.insert(event);


        // 3. 发送事务消息
        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
            "tx-event-group",
            "event-topic",
            MessageBuilder.withPayload(event.getId()).build(),
            event.getId()
        );


        if (!result.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE)) {
            throw new TransactionException("消息发送失败");
        }
    }
}

3.3 RocketMQ事务监听器

@RocketMQTransactionListener(txProducerGroup = "tx-event-group")
public class EventTransactionListener implements RocketMQTransactionListener {


    private final DistributedEventMapper eventMapper;


    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        Long eventId = (Long) arg;
        DistributedEvent event = eventMapper.selectById(eventId);


        if (event != null && EventStatus.NEW.name().equals(event.getStatus())) {
            return LocalTransactionState.COMMIT_MESSAGE;
        }
        return LocalTransactionState.ROLLBACK_MESSAGE;
    }


    @Override
    public LocalTransactionState checkLocalTransaction(Message msg) {
        Long eventId = Long.parseLong(new String(msg.getBody()));
        DistributedEvent event = eventMapper.selectById(eventId);


        if (event == null) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }


        return EventStatus.NEW.name().equals(event.getStatus()) ? 
            LocalTransactionState.COMMIT_MESSAGE : 
            LocalTransactionState.ROLLBACK_MESSAGE;
    }
}

3.4 事件消费者

@Service
@RocketMQMessageListener(
    topic = "event-topic",
    consumerGroup = "event-consumer-group"
)
public class EventConsumer implements RocketMQListener<String> {


    private final EventDispatcher eventDispatcher;
    private final DistributedEventMapper eventMapper;


    @Override
    @Transactional
    public void onMessage(String message) {
        Long eventId = Long.parseLong(message);
        DistributedEvent event = eventMapper.selectById(eventId);


        // 幂等性检查
        if (event == null || !EventStatus.NEW.name().equals(event.getStatus())) {
            return;
        }


        // 更新状态为处理中
        event.setStatus(EventStatus.PROCESSING.name());
        eventMapper.updateById(event);


        try {
            // 分发事件处理
            eventDispatcher.dispatch(event);


            // 处理成功
            event.setStatus(EventStatus.SUCCESS.name());
        } catch (Exception e) {
            // 处理失败
            event.setStatus(EventStatus.FAILED.name());
            event.setRetryCount(event.getRetryCount() + 1);
        }


        eventMapper.updateById(event);
    }
}

3.5 事件分发器

@Component
public class EventDispatcher {


    private final Map<String, EventHandler> handlers = new ConcurrentHashMap<>();


    // 注册处理器
    public void registerHandler(String eventType, EventHandler handler) {
        handlers.put(eventType, handler);
    }


    public void dispatch(DistributedEvent event) {
        EventHandler handler = handlers.get(event.getEventType());
        if (handler == null) {
            throw new EventHandleException("未找到事件处理器: " + event.getEventType());
        }


        handler.handle(event);
    }
}


// 订单创建事件处理器
@Component
public class OrderCreatedHandler implements EventHandler {


    private final PaymentService paymentService;


    @Override
    public void handle(DistributedEvent event) {
        OrderCreatedEvent payload = JSON.parseObject(event.getPayload(), OrderCreatedEvent.class);
        paymentService.createPayment(payload.getOrderId(), payload.getAmount());
    }
}

3.6 补偿任务(定时重试)

@Slf4j
@Component
public class EventCompensator {


    private final EventDispatcher eventDispatcher;
    private final DistributedEventMapper eventMapper;
    private final RocketMQTemplate rocketMQTemplate;


    @Scheduled(fixedDelay = 30000) // 每30秒执行一次
    public void compensateFailedEvents() {
        // 查询失败且重试次数小于5次的事件
        List<DistributedEvent> failedEvents = eventMapper.selectList(
            new QueryWrapper<DistributedEvent>()
                .eq("status", EventStatus.FAILED.name())
                .lt("retry_count", 5)
        );


        for (DistributedEvent event : failedEvents) {
            try {
                log.info("重试事件: {}", event.getId());
                rocketMQTemplate.syncSend("event-topic", event.getId().toString());
            } catch (Exception e) {
                log.error("事件重试发送失败: {}", event.getId(), e);
            }
        }
    }
}

四、应用场景实战

4.1 电商下单场景

image.png

代码实现:

// 订单服务
@Service
public class OrderService {


    private final TransactionCoordinator coordinator;


    public void createOrder(Order order) {
        coordinator.executeInTransaction(() -> {
            // 1. 保存订单
            orderMapper.insert(order);


            // 2. 生成事件数据
            OrderCreatedEvent event = new OrderCreatedEvent();
            event.setOrderId(order.getId());
            event.setAmount(order.getAmount());


        }, "ORDER_CREATED", event);
    }
}


// 支付服务
@Component
public class PaymentHandler implements EventHandler {


    @Override
    public void handle(DistributedEvent event) {
        OrderCreatedEvent payload = JSON.parseObject(event.getPayload(), OrderCreatedEvent.class);
        paymentService.createPayment(payload.getOrderId(), payload.getAmount());
    }
}

4.2 跨行转账场景

// 转账服务
public void transfer(TransferRequest request) {
    coordinator.executeInTransaction(() -> {
        // 1. 扣减转出账户
        accountService.debit(request.getFromAccount(), request.getAmount());


        // 2. 生成转账事件
        TransferEvent event = new TransferEvent();
        event.setFromAccount(request.getFromAccount());
        event.setToAccount(request.getToAccount());
        event.setAmount(request.getAmount());


    }, "TRANSFER_INITIATED", event);
}


// 收款银行服务
@Component
public class TransferHandler implements EventHandler {


    @Override
    public void handle(DistributedEvent event) {
        TransferEvent payload = JSON.parseObject(event.getPayload(), TransferEvent.class);


        // 调用银行API
        bankService.credit(payload.getToAccount(), payload.getAmount());
    }
}

4.3 酒店预订场景

// 预订服务
public void bookHotel(BookingRequest request) {
    coordinator.executeInTransaction(() -> {
        // 1. 保存预订记录
        bookingMapper.insert(booking);


        // 2. 生成支付事件
        PaymentEvent paymentEvent = new PaymentEvent();
        paymentEvent.setBookingId(booking.getId());
        paymentEvent.setAmount(booking.getAmount());


    }, "BOOKING_CREATED", paymentEvent);


    // 3. 生成积分事件
    PointEvent pointEvent = new PointEvent();
    pointEvent.setUserId(request.getUserId());
    pointEvent.setPoints(booking.getAmount() / 10);
    coordinator.executeInTransaction(() -> {}, "POINT_EVENT", pointEvent);
}


// 积分服务
@Component
public class PointHandler implements EventHandler {


    @Override
    public void handle(DistributedEvent event) {
        PointEvent payload = JSON.parseObject(event.getPayload(), PointEvent.class);
        pointService.addPoints(payload.getUserId(), payload.getPoints());
    }
}

五、高级特性实现

5.1 幂等性设计

public class IdempotentHandler implements EventHandler {


    private final DistributedEventMapper eventMapper;


    @Override
    public void handle(DistributedEvent event) {
        // 检查是否已处理过
        if (eventMapper.selectById(event.getId()) != null) {
            log.warn("重复事件已忽略: {}", event.getId());
            return;
        }


        // 处理逻辑...
    }
}

5.2 死信队列处理

@Bean
public MessageChannel deadLetterChannel() {
    return MessageChannels.queue().get();
}


@Bean
@ServiceActivator(inputChannel = "deadLetterChannel")
public MessageHandler deadLetterHandler() {
    return message -> {
        // 处理无法投递的消息
        log.error("死信消息: {}", message);
        DeadLetter deadLetter = new DeadLetter();
        deadLetter.setPayload(message.getPayload().toString());
        deadLetterRepository.save(deadLetter);
    };
}

5.3 事件溯源

@Entity
public class EventSourcingRecord {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String aggregateId; // 聚合根ID
    private String eventType;
    private String payload;
    private LocalDateTime timestamp;
}


public void saveEvent(String aggregateId, String eventType, Object payload) {
    EventSourcingRecord record = new EventSourcingRecord();
    record.setAggregateId(aggregateId);
    record.setEventType(eventType);
    record.setPayload(JSON.toJSONString(payload));
    record.setTimestamp(LocalDateTime.now());
    eventSourcingRepository.save(record);
}

六、性能优化策略

6.1 批量事件处理

@RocketMQMessageListener(
    topic = "event-topic",
    consumerGroup = "batch-consumer",
    consumeMode = ConsumeMode.ORDERLY,
    messageModel = MessageModel.CLUSTERING,
    selectorExpression = "*",
    consumeThreadMax = 20
)
public class BatchEventConsumer implements RocketMQListener<List<MessageExt>> {


    @Override
    public void onMessage(List<MessageExt> messages) {
        List<Long> eventIds = messages.stream()
            .map(msg -> Long.parseLong(new String(msg.getBody())))
            .collect(Collectors.toList());


        // 批量查询事件
        List<DistributedEvent> events = eventMapper.selectBatchIds(eventIds);


        // 批量处理
        eventDispatcher.batchDispatch(events);
    }
}

6.2 事件表分片设计

// 按月份分片
@TableName("distributed_event_#{T(java.time.LocalDate).now().getMonthValue()}")
public class DistributedEvent {
    // ...
}


// 动态表名处理器
public class MonthShardingTableNameHandler implements ITableNameHandler {


    @Override
    public String dynamicTableName(String sql, String tableName) {
        int month = LocalDate.now().getMonthValue();
        return tableName + "_" + month;
    }
}

6.3 异步事件处理

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {


    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix("EventExecutor-");
        executor.initialize();
        return executor;
    }
}


// 异步处理事件
@Async
@Override
public void handle(DistributedEvent event) {
    // 事件处理逻辑
}

七、生产环境最佳实践

7.1 监控指标配置

@Bean
public MeterRegistryCustomizer<MeterRegistry> metrics() {
    return registry -> {
        Gauge.builder("event.queue.size", eventMapper::selectPendingCount)
            .description("待处理事件数量")
            .register(registry);


        Gauge.builder("event.process.duration", eventDispatcher::getAvgProcessTime)
            .description("事件平均处理时间")
            .register(registry);
    };
}

7.2 部署架构

image.png

7.3 配置建议

rocketmq:
  name-server: mq1:9876;mq2:9876;mq3:9876
  producer:
    group: tx-producer-group
    send-message-timeout: 3000
  consumer:
    group: event-consumer-group
    consume-thread-max: 32


event:
  max-retry: 5
  retry-interval: 30000 # 30秒
  sharding-strategy: monthly # 分片策略

八、与传统方案对比

image.png

九、总结与展望

9.1 方案优势

  1. 高性能:单机支持万级TPS
  2. 低耦合:服务间通过消息解耦
  3. 高可用:无单点故障
  4. 可扩展:水平扩展能力强
  5. 简单易用:Spring Boot无缝集成

9.2 适用场景

  • 电商订单系统
  • 跨行转账业务
  • 酒店机票预订
  • 物联网设备联动
  • 微服务间数据同步

9.3 未来演进

  1. 事件溯源增强:完整业务追溯能力
  2. AI驱动补偿:智能故障预测与修复
  3. 跨链事务:区块链集成
  4. 无服务架构:Serverless适配