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

时间:2017-05-19 08:35:52

标签: spring-cloud microservices distributed-transactions hystrix spring-cloud-netflix

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

现在有买票的功能,主要流程如下:
  1.订单服务接受创建订单的请求
  2.订单服务创建具有待处理状态的订单实体   3.订购服务电话用户服务处理用户付款   4.订购服务电话票务服务以更新用户票   5.订单服务将订单实体更新为完成。

我想使用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;
    }
}

这里的一些要点是:

  1. @HystrixCommand方法中使用OrderResource.create(order),使用fallback功能。
  2. 如果创建时出现错误,order中使用的OrderResource.create(order)实例将在后备功能中再次使用。虽然这个order的持久性将得到支持。但是,此实例中的数据仍可用于检查运行情况。
  3. 所以我使用状态:'PENDING','PAID','FINISHED'来检查是否进行了一些服务呼叫。
  4. ticketCompositeServiceuserCompositeService是假装客户。对于假装客户端方法payForOrder(),还有另一种方法payForOrderFallback()用于回退。
  5. 我需要确保可以多次调用回退方法。
  6. 我为try/catchticketCompositeService来电添加了userCompositeService,以确保订单将以'FAILED'状态保存。
  7. 这个解决方案似乎可以在大多数时间使用。除此之外,在回退函数中,如果userCompositeService.payForOrderFallback(payDTO);中存在某些错误,则不会调用以下复合服务调用。

    另一个问题是,我觉得它太复杂了。

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

1 个答案:

答案 0 :(得分:1)

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

这种方法不能提供任何弹性。由于涉及外部各方,来自数据库的ACID保证是不够的,并且Hystrix后备不会保护您免受任何不属于您的代码的任何事情。

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

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