【问题标题】:How to implement distributed transaction with hystrix fallback based on Spring Cloud architect基于 Spring Cloud 架构师如何使用 hystrix fallback 实现分布式事务
【发布时间】:2017-10-19 07:07:36
【问题描述】:

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

现在有了买票功能,主要流程如下:
1.订单服务接受创建订单的请求
2. 订单服务创建处于待处理状态的订单实体。
3.订单服务调用用户服务处理用户付款。
4.订单服务调用ticket service更新用户ticket。
5. order service 将 order 实体更新为 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);
  }
}

然后是 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. OrderResource.create(order)方法中使用@HystrixCommand,带有fallback函数。
  2. 如果创建过程中出现错误,OrderResource.create(order) 中使用的order 实例将在回退功能中再次使用。虽然这个order 的持久性会被回滚。但是这个实例中的数据仍然可以用来检查运行情况。
  3. 所以我使用状态:'PENDING'、'PAID'、'FINISHED'来检查是否进行了某些服务调用。
  4. ticketCompositeServiceuserCompositeService 是假客户。对于 feign 客户端方法 payForOrder(),还有另一种方法 payForOrderFallback() 用于回退。
  5. 我需要确保可以多次调用回退方法。
  6. 我为ticketCompositeServiceuserCompositeService 调用添加了try/catch,以确保订单将以“失败”状态保存。

似乎这个解决方案大部分时间都可以工作。除此之外,在fallback函数中,如果userCompositeService.payForOrderFallback(payDTO);有错误,那么下面的复合服务调用将不会被调用。

而且,另一个问题是,我认为它太复杂了。

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

【问题讨论】:

  • 你应该试试 Event Sourcing + CQRS 技术link

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


【解决方案1】:

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

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

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

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

【讨论】:

  • 谢谢,我也对此进行了很多调查,写了很多测试代码,我最终的解决方案也是使用一些MQ和事件驱动的过程。谢谢。
猜你喜欢
  • 2017-08-22
  • 2019-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-03
  • 2017-06-27
  • 2014-12-28
相关资源
最近更新 更多