如何使用基于Spring Cloud架构的hystrix回退实现分布式事务

Mav*_*arn 5 distributed-transactions hystrix microservices spring-cloud spring-cloud-netflix

我正在使用spring cloud来实现我的微服务系统,一个售票平台.场景是,有一个zuul代理,一个eureka注册表和3个服务:用户服务,订单服务和票务服务.服务使用假装声明式REST客户端相互通信.

现在有一个买票的功能,主要流程如下:
1.订单服务接受请求创建订单
2.订单服务创建订单实体具有挂单状态.
3.订购服务呼叫用户服务来处理用户付费.
4.订购服务电话票务服务以更新用户票证.
5.订单服务将订单实体更新为FINISHED.

我想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);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的一些关键点是:

  1. @HystrixCommandOrderResource.create(order)方法中使用,具有fallback功能.
  2. 如果创建中存在某些错误,则将在回退功能中再次order使用所使用的实例 OrderResource.create(order).虽然这种持久性order将得到支持.但是,此实例中的数据仍可用于检查运行情况.
  3. 所以我使用状态:'PENDING','PAID','FINISHED'来检查是否进行了一些服务呼叫.
  4. ticketCompositeService并且userCompositeService是假装客户.对于假装客户端方法payForOrder(),还有另一种payForOrderFallback()回退方法.
  5. 我需要确保可以多次调用回退方法.
  6. 我想补充try/catchticketCompositeService,并userCompositeService呼吁,以确保订单将反正与"失败"的状态被保存.

似乎这个解决方案可以在大多数时间使用.除此之外,在回退功能中,如果存在一些错误userCompositeService.payForOrderFallback(payDTO);,则不会调用以下复合服务调用.

而另一个问题是,我认为这太复杂了.

因此,对于这种情况,我应该如何正确有效地实现dist事务.任何建议或建议都会有所帮助.谢谢.

jih*_*hor 1

在 Hystrix 回退中编写补偿逻辑是危险的,因为不涉及持久性。

这种方法不提供任何弹性。由于涉及外部各方,来自数据库的 ACID 保证在这里是不够的,并且 Hystrix 回退不会保护您免受代码之外的任何内容的影响。

例如,如果您的解决方案kill -9在付款完成后遇到中断(例如停电或简单的情况),您将丢失订单和补偿逻辑,这意味着订单将被支付,但不会出现在数据库中。

更具弹性的方法将涉及任何流行的消息代理来进行事件驱动的交付,并在处理逻辑中进行一些重复数据删除,以确保在中断后重新交付事件时提供一次性的服务质量。