微服务架构分布式事务实战:本地消息表方案从设计到落地全流程

微服务架构解决了单体应用的扩展瓶颈,但引入了分布式事务、服务治理等新问题。本文以Spring Boot框架为基座,覆盖高并发设计、消息中间件集成和分布式事务处理,提供经过验证的工程方案。

一、微服务拆分原则与Spring Boot项目搭建

微服务拆分的核心原则是”高内聚低耦合”,按业务领域而非技术层划分服务。拆分粒度太粗失去微服务意义,太细则运维成本激增。实操中建议按限界上下文(Bounded Context)拆分,每个服务对应一个业务子域。

Spring Boot 3.x项目标准结构:

order-service/
├── pom.xml
├── src/main/java/com/yunthe/order/
│   ├── OrderApplication.java         # 启动类
│   ├── config/                       # 配置类
│   ├── controller/                   # REST接口层
│   ├── service/                      # 业务逻辑层
│   │   └── impl/
│   ├── repository/                   # 数据访问层
│   ├── domain/                       # 领域模型
│   │   ├── entity/
│   │   ├── dto/
│   │   └── event/
│   ├── client/                       # Feign远程调用
│   └── common/                       # 公共组件
└── src/main/resources/
    ├── application.yml
    └── mapper/                       # MyBatis映射文件

API接口规范遵循RESTful风格,统一响应格式:

@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    @PostMapping
    public Result<OrderVO> createOrder(@Valid @RequestBody CreateOrderDTO dto) {
        return Result.success(orderService.createOrder(dto));
    }

    @GetMapping("/{id}")
    public Result<OrderVO> getOrder(@PathVariable Long id) {
        return Result.success(orderService.getById(id));
    }

    @GetMapping
    public Result<PageResult<OrderVO>> listOrders(
            @Valid OrderQueryDTO query,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "20") int size) {
        return Result.success(orderService.pageQuery(query, page, size));
    }
}

// 统一响应体
@Data
@Schema(description = "统一响应结构")
public class Result<T> {
    private int code;
    private String message;
    private T data;
    private long timestamp;

    public static <T> Result<T> success(T data) {
        Result<T> r = new Result<>();
        r.setCode(200);
        r.setMessage("success");
        r.setData(data);
        r.setTimestamp(System.currentTimeMillis());
        return r;
    }

    public static <T> Result<T> error(int code, String message) {
        Result<T> r = new Result<>();
        r.setCode(code);
        r.setMessage(message);
        return r;
    }
}

二、高并发设计:缓存与限流降级

高并发设计的三板斧是缓存、异步、降级。缓存层使用Redis,核心是保证缓存与数据库一致性。推荐延迟双删策略:

// Redis缓存操作 - 延迟双删保证一致性
@Service
@RequiredArgsConstructor
public class ProductCacheService {

    private final StringRedisTemplate redisTemplate;
    private final ProductMapper productMapper;
    private static final long CACHE_TTL = 3600; // 1小时
    private static final long DOUBLE_DELETE_DELAY = 500; // 500ms

    public Product getProduct(Long id) {
        String key = "product:" + id;
        String cached = redisTemplate.opsForValue().get(key);
        if (cached != null) {
            return JSON.parseObject(cached, Product.class);
        }
        // 缓存未命中 - 查询数据库
        Product product = productMapper.selectById(id);
        if (product != null) {
            // 防止缓存穿透 - 空值也缓存,短TTL
            redisTemplate.opsForValue().set(key,
                JSON.toJSONString(product),
                product.getId() == null ? 60 : CACHE_TTL,
                TimeUnit.SECONDS);
        }
        return product;
    }

    @Transactional
    public void updateProduct(Product product) {
        // 第一次删除缓存
        String key = "product:" + product.getId();
        redisTemplate.delete(key);

        // 更新数据库
        productMapper.updateById(product);

        // 延迟第二次删除(异步执行避免阻塞)
        CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(DOUBLE_DELETE_DELAY);
                redisTemplate.delete(key);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
}

限流使用Sentinel,支持QPS限流、线程数限流和熔断降级。以下为Sentinel资源保护配置:

@Configuration
public class SentinelConfig {

    @PostConstruct
    public void initRules() {
        // 订单创建接口限流: QPS 100
        FlowRule orderRule = new FlowRule();
        orderRule.setResource("createOrder");
        orderRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        orderRule.setCount(100);

        // 熔断规则: 慢调用比例阈值0.5, RT阈值200ms
        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource("createOrder");
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        degradeRule.setCount(200);        // 最大响应时间200ms
        degradeRule.setSlowRatioThreshold(0.5);
        degradeRule.setTimeWindow(10);    // 熔断持续时间10秒
        degradeRule.setMinRequestAmount(20);
        degradeRule.setStatIntervalMs(1000);

        FlowRuleManager.loadRules(List.of(orderRule));
        DegradeRuleManager.loadRules(List.of(degradeRule));
    }
}

三、消息中间件:异步解耦与削峰填谷

消息中间件是高并发场景削峰的关键组件。RabbitMQ适合复杂路由场景,Kafka适合高吞吐日志流,RocketMQ在电商订单场景表现优秀。以RocketMQ处理订单异步流程为例:

// 订单服务 - 发送事务消息
@Service
@RequiredArgsConstructor
public class OrderService {

    private final RocketMQTemplate rocketMQTemplate;
    private final OrderMapper orderMapper;

    @Transactional
    public Order createOrder(CreateOrderDTO dto) {
        Order order = buildOrder(dto);
        orderMapper.insert(order);

        // 发送事务消息确保最终一致性
        Message<OrderCreatedEvent> message = MessageBuilder
            .withPayload(new OrderCreatedEvent(order.getId(), order.getUserId()))
            .build();

        rocketMQTemplate.sendMessageInTransaction(
            "order-topic:created",
            message,
            order  // 传递给本地事务执行器
        );
        return order;
    }
}

// 事务消息监听器 - 本地事务执行+回查
@RocketMQTransactionListener
public class OrderTransactionListener
        implements RocketMQLocalTransactionListener {

    @Resource
    private OrderMapper orderMapper;

    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(
            Message msg, Object arg) {
        Order order = (Order) arg;
        try {
            // 本地事务已在外层@Transactional中执行
            // 此处可以做一些附加操作,如扣减库存
            return RocketMQLocalTransactionState.COMMIT;
        } catch (Exception e) {
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }

    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        // 事务回查 - 检查订单是否创建成功
        String orderId = msg.getHeaders().get("orderId", String.class);
        Order order = orderMapper.selectById(orderId);
        return order != null
            ? RocketMQLocalTransactionState.COMMIT
            : RocketMQLocalTransactionState.ROLLBACK;
    }
}

四、分布式事务:Saga模式实战

分布式事务在微服务架构下不可完全避免。2PC性能差且锁定资源,TCC开发成本高。Saga模式通过补偿事务实现最终一致性,适合长流程业务场景。Seata框架的Saga模式工作流编排方案如下:

// Saga业务流程定义 - 订单创建流程
// 步骤1: 创建订单 - 补偿: 删除订单
// 步骤2: 扣减库存 - 补偿: 恢复库存
// 步骤3: 扣减积分 - 补偿: 恢复积分
// 步骤4: 发送通知 - 补偿: 无(不可补偿操作置于最后)

@RestController
@RequestMapping("/api/v1/saga")
@RequiredArgsConstructor
public class SagaOrderController {

    private final SagaCoordinator sagaCoordinator;

    @PostMapping("/order")
    public Result<String> createOrderSaga(@RequestBody CreateOrderDTO dto) {
        String sagaId = sagaCoordinator.start("order-create-saga", Map.of(
            "userId", dto.getUserId(),
            "productId", dto.getProductId(),
            "quantity", dto.getQuantity(),
            "totalAmount", dto.getTotalAmount()
        ));
        return Result.success(sagaId);
    }
}

// Saga补偿示例 - 库存服务
@Service
public class InventorySagaService {

    @SagaAction(name = "deductInventory", compensate = "restoreInventory")
    public boolean deductInventory(Long productId, int quantity) {
        int rows = inventoryMapper.deductStock(productId, quantity);
        if (rows == 0) {
            throw new BusinessException("库存不足");
        }
        return true;
    }

    @SagaCompensate(forAction = "deductInventory")
    public boolean restoreInventory(Long productId, int quantity) {
        inventoryMapper.restoreStock(productId, quantity);
        return true;
    }
}

服务治理还包括链路追踪。通过Spring Cloud Sleuth + Zipkin或SkyWalking实现全链路调用追踪,定位跨服务调用延迟瓶颈:

# application.yml - 链路追踪配置
spring:
  sleuth:
    sampler:
      probability: 1.0    # 生产环境建议0.1采样率
    propagation:
      type: B3            # 使用B3传播协议
  zipkin:
    base-url: http://zipkin.observability.svc:9411
    service:
      name: order-service

# 日志格式注入TraceId
logging:
  pattern:
    level: "%5p [${spring.application.name},%X{traceId:-},%X{spanId:-}]"

微服务架构没有银弹。Spring Boot框架提供了丰富的技术组件,但高并发设计需要根据实际业务特点选择性地使用缓存、限流、异步消息等手段。业务中台建设的关键是定义清晰的服务边界和接口契约,分布式事务的处理策略应遵循”能异步不同步,能补偿不锁定”的原则。服务治理是一个持续优化的过程,需配合监控数据和压测结果不断调整阈值和策略。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/wei-fu-wu-jia-gou-fen-bu-shi-shi-wu-shi-zhan-ben-di-xiao-xi/

(0)
小编小编
上一篇 17小时前
下一篇 17小时前

相关推荐

微服务架构分布式事务实战:本地消息表方案从设计到落地全流程

微服务架构在高并发场景下的最大挑战不是拆分服务,而是拆分后的分布式事务一致性。本文以Spring Boot框架下的电商订单系统为例,从问题拆解、方案选型到代码实现,完整演示分布式事务的处理过程,覆盖消息中间件、服务治理等后端开发核心技能。

分布式事务问题场景拆解

电商下单流程涉及三个微服务:订单服务创建订单、库存服务扣减库存、账户服务扣减余额。任何一个步骤失败,都需要回滚已执行的操作。在单体架构中,一个本地事务就能搞定;微服务架构下,这三个操作分布在不同的数据库实例中,本地事务无法覆盖。

下单流程:
1. 订单服务 → 创建订单(订单库)
2. 库存服务 → 扣减库存(库存库)
3. 账户服务 → 扣减余额(账户库)

异常场景:
- 步骤2失败 → 步骤1需要回滚
- 步骤3失败 → 步骤1和2都需要回滚
- 网络超时 → 无法确定对方是否执行成功

方案选型:TCC vs Saga vs 本地消息表

高并发设计中,三种主流方案各有适用场景:

方案对比:

TCC(Try-Confirm-Cancel)
  适用:强一致性要求高、并发量中等
  优点:数据一致性好
  缺点:业务侵入大,每个服务要写三个接口

Saga
  适用:长事务、跨多个服务
  优点:业务侵入较小
  缺点:最终一致,中间状态可见

本地消息表 + 消息中间件
  适用:允许最终一致性、高并发场景
  优点:性能好,业务侵入小
  缺点:只能保证最终一致

本文选用本地消息表方案。核心思路:订单服务在本地事务中同时写入订单和消息表,消息中间件异步通知库存和账户服务。通过消息重试机制保证最终一致性。

订单服务:本地消息表实现

Spring Boot框架下,使用JPA实现订单服务和消息表的本地事务。

// 订单实体
@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String orderNo;
    private Long userId;
    private Long productId;
    private Integer quantity;
    private BigDecimal amount;
    private String status; // CREATED, PAID, CANCELLED

    // getter/setter 省略
}

// 本地消息表实体
@Entity
@Table(name = "message_outbox")
public class MessageOutbox {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String messageId;       // 全局唯一ID
    private String topic;           // 消息主题
    private String payload;         // 消息内容JSON
    private Integer retryCount;     // 重试次数
    private String status;          // PENDING, SENT, FAILED
    private LocalDateTime createTime;
    private LocalDateTime nextRetryTime;

    // getter/setter 省略
}
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private MessageOutboxRepository messageOutboxRepository;

    @Transactional
    public Order createOrder(OrderRequest request) {
        // 1. 创建订单
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setUserId(request.getUserId());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setAmount(request.getAmount());
        order.setStatus("CREATED");
        orderRepository.save(order);

        // 2. 同一事务写入消息表
        MessageOutbox message = new MessageOutbox();
        message.setMessageId(UUID.randomUUID().toString());
        message.setTopic("order.created");
        message.setPayload(toJson(Map.of(
            "orderId", order.getId(),
            "orderNo", order.getOrderNo(),
            "productId", order.getProductId(),
            "quantity", order.getQuantity(),
            "userId", order.getUserId(),
            "amount", order.getAmount()
        )));
        message.setRetryCount(0);
        message.setStatus("PENDING");
        message.setCreateTime(LocalDateTime.now());
        message.setNextRetryTime(LocalDateTime.now());
        messageOutboxRepository.save(message);

        return order;
    }

    private String generateOrderNo() {
        return "ORD" + System.currentTimeMillis()
            + String.format("%04d", new Random().nextInt(10000));
    }

    private String toJson(Object obj) {
        try {
            return new ObjectMapper().writeValueAsString(obj);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

消息中间件:RocketMQ异步投递

消息中间件选用RocketMQ,支持事务消息和延迟消息,适合分布式事务场景。消息投递服务定时扫描消息表,将PENDING状态的消息发送到RocketMQ。

@Service
public class MessageSender {

    @Autowired
    private MessageOutboxRepository messageOutboxRepository;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Scheduled(fixedDelay = 5000)
    public void sendPendingMessages() {
        List<MessageOutbox> messages = messageOutboxRepository
            .findByStatusAndNextRetryTimeBefore("PENDING", LocalDateTime.now());

        for (MessageOutbox msg : messages) {
            try {
                rocketMQTemplate.convertAndSend(msg.getTopic(), msg.getPayload());
                msg.setStatus("SENT");
                messageOutboxRepository.save(msg);
            } catch (Exception e) {
                msg.setRetryCount(msg.getRetryCount() + 1);
                if (msg.getRetryCount() >= 5) {
                    msg.setStatus("FAILED");
                } else {
                    // 指数退避
                    msg.setNextRetryTime(LocalDateTime.now()
                        .plusSeconds((long) Math.pow(2, msg.getRetryCount())));
                }
                messageOutboxRepository.save(msg);
            }
        }
    }
}

库存服务:消费消息扣减库存

库存服务监听 order.created 主题,收到消息后扣减库存。需要实现幂等性,防止消息重复消费。

@RocketMQMessageListener(
    topic = "order.created",
    consumerGroup = "inventory-consumer-group"
)
@Component
public class InventoryConsumer implements RocketMQListener<String> {

    @Autowired
    private InventoryRepository inventoryRepository;

    @Autowired
    private IdempotentRepository idempotentRepository;

    @Override
    @Transactional
    public void onMessage(String payload) {
        OrderMessage msg = parseJson(payload, OrderMessage.class);

        // 幂等性检查:用messageId作为幂等键
        if (idempotentRepository.existsByMessageId(msg.getMessageId())) {
            return; // 已处理过,跳过
        }

        // 扣减库存
        Inventory inventory = inventoryRepository
            .findByProductId(msg.getProductId())
            .orElseThrow(() -> new RuntimeException("库存记录不存在"));

        if (inventory.getStock() < msg.getQuantity()) {
            // 库存不足,发送补偿消息
            throw new RuntimeException("库存不足");
        }

        inventory.setStock(inventory.getStock() - msg.getQuantity());
        inventoryRepository.save(inventory);

        // 记录幂等键
        IdempotentRecord record = new IdempotentRecord();
        record.setMessageId(msg.getMessageId());
        record.setHandler("inventory");
        idempotentRepository.save(record);
    }

    private <T> T parseJson(String json, Class<T> clazz) {
        try {
            return new ObjectMapper().readValue(json, clazz);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

服务治理:熔断降级配置

微服务架构中,服务治理是保障系统稳定性的关键。当库存服务不可用时,需要熔断降级,避免雪崩。Spring Boot框架集成Sentinel实现熔断:

// application.yml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.1.100:8858
      eager: true

# 熔断规则配置
csp:
  sentinel:
    flow:
      - resource: deductInventory
        count: 100          # QPS限流阈值
        grade: 1            # QPS模式
    degrade:
      - resource: deductInventory
        count: 0.5          # 异常比例阈值
        timeWindow: 10      # 熔断时长10秒
        grade: 1            # 异常比例模式
        minRequestAmount: 5 # 最小请求数
@Service
public class InventoryService {

    @SentinelResource(
        value = "deductInventory",
        blockHandler = "deductInventoryBlockHandler",
        fallback = "deductInventoryFallback"
    )
    public boolean deductInventory(Long productId, Integer quantity) {
        // 正常扣减逻辑
        return inventoryRepository.deduct(productId, quantity);
    }

    // 限流处理
    public boolean deductInventoryBlockHandler(
            Long productId, Integer quantity, BlockException ex) {
        log.warn("库存服务被限流: productId={}", productId);
        throw new ServiceException("系统繁忙,请稍后重试");
    }

    // 降级处理
    public boolean deductInventoryFallback(
            Long productId, Integer quantity, Throwable e) {
        log.error("库存服务降级: productId={}, error={}", productId, e.getMessage());
        // 降级策略:记录到待处理队列,异步补偿
        compensationQueue.offer(new DeductTask(productId, quantity));
        return false;
    }
}

API接口规范设计

业务中台建设中,统一的API接口规范是基础。所有微服务遵循统一的响应格式和错误码体系:

// 统一响应体
public class ApiResponse<T> {
    private int code;       // 0表示成功,非0表示错误
    private String message;
    private T data;
    private String traceId; // 链路追踪ID

    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> resp = new ApiResponse<>();
        resp.code = 0;
        resp.message = "success";
        resp.data = data;
        resp.traceId = MDC.get("traceId");
        return resp;
    }

    public static <T> ApiResponse<T> error(int code, String message) {
        ApiResponse<T> resp = new ApiResponse<>();
        resp.code = code;
        resp.message = message;
        resp.traceId = MDC.get("traceId");
        return resp;
    }
}

// 错误码定义
public enum ErrorCode {
    PARAM_INVALID(40001, "参数校验失败"),
    UNAUTHORIZED(40101, "未登录或token过期"),
    FORBIDDEN(40301, "无权限访问"),
    NOT_FOUND(40401, "资源不存在"),
    INVENTORY_SHORTAGE(40901, "库存不足"),
    SYSTEM_ERROR(50001, "系统内部错误");

    private final int code;
    private final String message;
}

分布式事务监控与告警

本地消息表方案中,需要监控消息积压和失败情况。以下是关键监控SQL和告警逻辑:

-- 监控PENDING消息积压(超过10分钟未发送)
SELECT COUNT(*) AS pending_count
FROM message_outbox
WHERE status = 'PENDING'
  AND create_time < DATE_SUB(NOW(), INTERVAL 10 MINUTE);

-- 监控FAILED消息数量
SELECT COUNT(*) AS failed_count
FROM message_outbox
WHERE status = 'FAILED';

-- 告警规则:
-- pending_count > 50 → 发送告警
-- failed_count > 0 → 立即告警,人工介入

分布式事务没有银弹。强一致性场景用TCC,高并发最终一致场景用本地消息表,长流程跨服务用Saga。业务中台建设中,不要追求一个方案解决所有问题,而是根据业务特点组合使用。消息中间件的选型、幂等性的设计、重试策略的配置,这些细节决定了分布式事务方案在生产环境中的可靠程度。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/wei-fu-wu-jia-gou-fen-bu-shi-shi-wu-shi-zhan-ben-di-xiao-xi/

(0)
小编小编
上一篇 18小时前
下一篇 18小时前

相关推荐