我正在使用spring cloud来实现我的微服务系统,一个售票平台。场景是,有一个zuul代理,一个eureka注册表和3个服务:用户服务,订单服务和票务服务。服务使用假装声明式REST客户端相互通信。
现在有买票的功能,主要流程如下:我想使用Hystrix Fallback
来实现交易。例如,如果付款流程已完成,但在票证移动期间发生了一些错误。如何尊重用户付款和订单状态。因为用户付款是在其他服务中。
以下是我目前的解决方案,我不确定它是否合适。或者还有其他更好的方法。
首先,OrderResource:
@RestController
@RequestMapping("/api/order")
public class OrderResource {
@HystrixCommand(fallbackMethod = "createFallback")
@PostMapping(value = "/")
public Order create(@RequestBody Order order) {
return orderService.create(order);
}
private Order createFallback(Order order) {
return orderService.createFallback(order);
}
}
然后是OrderService:
@Service
public class OrderService {
@Transactional
public Order create(Order order) {
order.setStatus("PENDING");
order = orderRepository.save(order);
UserPayDTO payDTO = new UserPayDTO();
userCompositeService.payForOrder(payDTO);
order.setStatus("PAID");
order = orderRepository.save(order);
ticketCompositeService.moveTickets(ticketIds, currentUserId);
order.setStatus("FINISHED");
order = orderRepository.save(order);
return order;
}
@Transactional
public Order createFallback(Order order) {
// order is the object processed in create(), there is Transaction in create(), so saving order will be rollback,
// but the order instance still exist.
if (order.getId() == null) { // order not saved even.
return null;
}
UserPayDTO payDTO = new UserPayDTO();
try {
if (order.getStatus() == "FINISHED") { // order finished, must be paid and ticket moved
userCompositeService.payForOrderFallback(payDTO);
ticketCompositeService.moveTicketsFallback(getTicketIdList(order.getTicketIds()), currentUserId);
} else if (order.getStatus() == "PAID") { // is paid, but not sure whether has error during ticket movement.
userCompositeService.payForOrderFallback(payDTO);
ticketCompositeService.moveTicketsFallback(getTicketIdList(order.getTicketIds()), currentUserId);
} else if (order.getStatus() == "PENDING") { // maybe have error during payment.
userCompositeService.payForOrderFallback(payDTO);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
order.setStatus("FAILED");
orderRepository.save(order); // order saving is rollbacked during create(), I save it here to trace the failed orders.
return order;
}
}
这里的一些要点是:
@HystrixCommand
方法中使用OrderResource.create(order)
,使用fallback
功能。 order
中使用的OrderResource.create(order)
实例将在后备功能中再次使用。虽然这个order
的持久性将得到支持。但是,此实例中的数据仍可用于检查运行情况。 ticketCompositeService
和userCompositeService
是假装客户。对于假装客户端方法payForOrder()
,还有另一种方法payForOrderFallback()
用于回退。 try/catch
和ticketCompositeService
来电添加了userCompositeService
,以确保订单将以'FAILED'状态保存。这个解决方案似乎可以在大多数时间使用。除此之外,在回退函数中,如果userCompositeService.payForOrderFallback(payDTO);
中存在某些错误,则不会调用以下复合服务调用。
另一个问题是,我觉得它太复杂了。
因此,对于这种情况,我应该如何正确有效地实现dist事务。任何建议或建议都会有所帮助。感谢。
答案 0 :(得分:1)
在Hystrix回退中编写补偿逻辑是危险的,因为不涉及持久性。
这种方法不能提供任何弹性。由于涉及外部各方,来自数据库的ACID保证是不够的,并且Hystrix后备不会保护您免受任何不属于您的代码的任何事情。
例如,如果您的解决方案在付款完成后遇到停电(例如停电或简单的kill -9
),您将失去订单和补偿逻辑,这意味着将支付订单,但不会出现在数据库中。
更具弹性的方法将涉及任何流行的消息代理,用于事件驱动的交付,以及处理逻辑中的一些重复数据删除,以确保在中断后重新传递事件时的一次性服务质量。