微服务架构下基于Saga模式实现霸王餐长事务的最终一致性在高并发的外卖霸王餐业务场景中业务流程往往横跨多个服务且执行时间较长典型的“下单-返利-核销”流程就是一个典型的长事务。传统的两阶段提交2PC或Seata AT模式由于需要长时间持有数据库锁在此类场景下极易导致系统吞吐量下降甚至死锁。Saga模式作为一种长事务解决方案通过将大事务拆分为多个本地事务并配合补偿机制能够有效解决这一痛点。业务流程与Saga模式设计外卖霸王餐的核心业务流程涉及三个主要步骤订单服务 (Order Service)创建霸王餐订单状态为“待支付”。用户服务 (User Service)发放返利增加用户账户余额或积分。核销服务 (Write-off Service)在特定条件下如订单完成触发核销逻辑。这三个步骤构成一个Saga事务。如果其中任意一步失败都需要触发前面步骤的补偿操作。例如如果核销失败则需要回滚返利如果返利失败则需要取消订单。基于状态机的Saga实现Seata提供了基于状态机的Saga模式实现通过JSON文件定义业务流程和状态转换具有高灵活性和可维护性。以下是订单创建流程的状态机定义示例{Name:CreateOrderSaga,Comment:创建霸王餐订单的Saga流程,StartState:CreateOrder,Version:0.0.1,States:{CreateOrder:{Type:ServiceTask,ServiceName:orderService,ServiceMethod:createOrder,CompensateState:CancelOrder,Next:GiveRebate},GiveRebate:{Type:ServiceTask,ServiceName:userService,ServiceMethod:giveRebate,CompensateState:RefundRebate,Next:WriteOff},WriteOff:{Type:ServiceTask,ServiceName:writeOffService,ServiceMethod:writeOff,Next:End},CancelOrder:{Type:ServiceTask,ServiceName:orderService,ServiceMethod:cancelOrder},RefundRebate:{Type:ServiceTask,ServiceName:userService,ServiceMethod:refundRebate}}}服务实现与异常处理在Java代码中我们需要实现上述状态机定义的服务方法。每个服务方法应只关注自身的业务逻辑并在失败时抛出异常由Seata框架触发相应的补偿状态。首先订单服务负责创建订单。如果创建失败直接抛出异常Saga流程终止。packagecom.jumei.bdb.order.service;importcom.jumei.bdb.order.dao.OrderDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/** * 订单服务实现 * author baodanbao.com.cn */ServicepublicclassOrderServiceImpl{AutowiredprivateOrderDaoorderDao;/** * 创建订单供Saga状态机调用 * param orderId 订单ID * param userId 用户ID * param itemId 商品ID * throws Exception 创建失败时抛出异常 */publicvoidcreateOrder(StringorderId,LonguserId,LongitemId)throwsException{booleansuccessorderDao.createOrder(orderId,userId,itemId);if(!success){thrownewException(订单创建失败业务ID: orderId);}}/** * 取消订单作为createOrder的补偿方法 * param orderId 订单ID */publicvoidcancelOrder(StringorderId){// 执行取消订单逻辑如更新订单状态为“已取消”orderDao.updateOrderStatus(orderId,CANCELLED);}}用户服务负责发放返利。如果返利发放失败框架会自动调用refundRebate方法进行补偿。packagecom.jumei.bdb.user.service;importcom.jumei.bdb.user.dao.UserAccountDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.math.BigDecimal;/** * 用户服务实现 * author baodanbao.com.cn */ServicepublicclassUserServiceImpl{AutowiredprivateUserAccountDaouserAccountDao;/** * 发放返利 * param userId 用户ID * param amount 返利金额 * throws Exception 发放失败时抛出异常 */publicvoidgiveRebate(LonguserId,BigDecimalamount)throwsException{booleansuccessuserAccountDao.addRebate(userId,amount);if(!success){thrownewException(返利发放失败用户ID: userId);}}/** * 退款返利作为giveRebate的补偿方法 * param userId 用户ID * param amount 返利金额 */publicvoidrefundRebate(LonguserId,BigDecimalamount){// 执行退款逻辑如扣除用户余额记录退款流水userAccountDao.deductRebate(userId,amount);}}核销服务作为流程的最后一步如果执行失败将触发前面所有步骤的回滚。packagecom.jumei.bdb.writeoff.service;importorg.springframework.stereotype.Service;/** * 核销服务实现 * author baodanbao.com.cn */ServicepublicclassWriteOffServiceImpl{/** * 执行核销逻辑 * param orderId 订单ID * throws Exception 核销失败时抛出异常 */publicvoidwriteOff(StringorderId)throwsException{// 模拟核销业务逻辑booleansuccessperformWriteOff(orderId);if(!success){thrownewException(核销失败订单ID: orderId);}}privatebooleanperformWriteOff(StringorderId){// 具体的核销实现returntrue;}}幂等性与持久化Saga模式下补偿操作可能会被重复执行例如网络超时导致的重试因此所有业务操作和补偿操作都必须是幂等的。在数据库设计层面应利用唯一索引或状态字段来防止重复操作。例如在插入返利流水时使用全局事务IDXID或业务唯一键作为唯一索引确保同一流水不会被重复插入。此外Saga状态机的执行状态需要被持久化Seata Server会负责存储和恢复状态机实例确保在系统崩溃后能够恢复并继续执行。本文著作权归 俱美开放平台 转载请注明出处
微服务架构下基于Saga模式实现霸王餐长事务的最终一致性
发布时间:2026/7/8 13:54:53
微服务架构下基于Saga模式实现霸王餐长事务的最终一致性在高并发的外卖霸王餐业务场景中业务流程往往横跨多个服务且执行时间较长典型的“下单-返利-核销”流程就是一个典型的长事务。传统的两阶段提交2PC或Seata AT模式由于需要长时间持有数据库锁在此类场景下极易导致系统吞吐量下降甚至死锁。Saga模式作为一种长事务解决方案通过将大事务拆分为多个本地事务并配合补偿机制能够有效解决这一痛点。业务流程与Saga模式设计外卖霸王餐的核心业务流程涉及三个主要步骤订单服务 (Order Service)创建霸王餐订单状态为“待支付”。用户服务 (User Service)发放返利增加用户账户余额或积分。核销服务 (Write-off Service)在特定条件下如订单完成触发核销逻辑。这三个步骤构成一个Saga事务。如果其中任意一步失败都需要触发前面步骤的补偿操作。例如如果核销失败则需要回滚返利如果返利失败则需要取消订单。基于状态机的Saga实现Seata提供了基于状态机的Saga模式实现通过JSON文件定义业务流程和状态转换具有高灵活性和可维护性。以下是订单创建流程的状态机定义示例{Name:CreateOrderSaga,Comment:创建霸王餐订单的Saga流程,StartState:CreateOrder,Version:0.0.1,States:{CreateOrder:{Type:ServiceTask,ServiceName:orderService,ServiceMethod:createOrder,CompensateState:CancelOrder,Next:GiveRebate},GiveRebate:{Type:ServiceTask,ServiceName:userService,ServiceMethod:giveRebate,CompensateState:RefundRebate,Next:WriteOff},WriteOff:{Type:ServiceTask,ServiceName:writeOffService,ServiceMethod:writeOff,Next:End},CancelOrder:{Type:ServiceTask,ServiceName:orderService,ServiceMethod:cancelOrder},RefundRebate:{Type:ServiceTask,ServiceName:userService,ServiceMethod:refundRebate}}}服务实现与异常处理在Java代码中我们需要实现上述状态机定义的服务方法。每个服务方法应只关注自身的业务逻辑并在失败时抛出异常由Seata框架触发相应的补偿状态。首先订单服务负责创建订单。如果创建失败直接抛出异常Saga流程终止。packagecom.jumei.bdb.order.service;importcom.jumei.bdb.order.dao.OrderDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/** * 订单服务实现 * author baodanbao.com.cn */ServicepublicclassOrderServiceImpl{AutowiredprivateOrderDaoorderDao;/** * 创建订单供Saga状态机调用 * param orderId 订单ID * param userId 用户ID * param itemId 商品ID * throws Exception 创建失败时抛出异常 */publicvoidcreateOrder(StringorderId,LonguserId,LongitemId)throwsException{booleansuccessorderDao.createOrder(orderId,userId,itemId);if(!success){thrownewException(订单创建失败业务ID: orderId);}}/** * 取消订单作为createOrder的补偿方法 * param orderId 订单ID */publicvoidcancelOrder(StringorderId){// 执行取消订单逻辑如更新订单状态为“已取消”orderDao.updateOrderStatus(orderId,CANCELLED);}}用户服务负责发放返利。如果返利发放失败框架会自动调用refundRebate方法进行补偿。packagecom.jumei.bdb.user.service;importcom.jumei.bdb.user.dao.UserAccountDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.math.BigDecimal;/** * 用户服务实现 * author baodanbao.com.cn */ServicepublicclassUserServiceImpl{AutowiredprivateUserAccountDaouserAccountDao;/** * 发放返利 * param userId 用户ID * param amount 返利金额 * throws Exception 发放失败时抛出异常 */publicvoidgiveRebate(LonguserId,BigDecimalamount)throwsException{booleansuccessuserAccountDao.addRebate(userId,amount);if(!success){thrownewException(返利发放失败用户ID: userId);}}/** * 退款返利作为giveRebate的补偿方法 * param userId 用户ID * param amount 返利金额 */publicvoidrefundRebate(LonguserId,BigDecimalamount){// 执行退款逻辑如扣除用户余额记录退款流水userAccountDao.deductRebate(userId,amount);}}核销服务作为流程的最后一步如果执行失败将触发前面所有步骤的回滚。packagecom.jumei.bdb.writeoff.service;importorg.springframework.stereotype.Service;/** * 核销服务实现 * author baodanbao.com.cn */ServicepublicclassWriteOffServiceImpl{/** * 执行核销逻辑 * param orderId 订单ID * throws Exception 核销失败时抛出异常 */publicvoidwriteOff(StringorderId)throwsException{// 模拟核销业务逻辑booleansuccessperformWriteOff(orderId);if(!success){thrownewException(核销失败订单ID: orderId);}}privatebooleanperformWriteOff(StringorderId){// 具体的核销实现returntrue;}}幂等性与持久化Saga模式下补偿操作可能会被重复执行例如网络超时导致的重试因此所有业务操作和补偿操作都必须是幂等的。在数据库设计层面应利用唯一索引或状态字段来防止重复操作。例如在插入返利流水时使用全局事务IDXID或业务唯一键作为唯一索引确保同一流水不会被重复插入。此外Saga状态机的执行状态需要被持久化Seata Server会负责存储和恢复状态机实例确保在系统崩溃后能够恢复并继续执行。本文著作权归 俱美开放平台 转载请注明出处