5

스프링 클라우드를 사용하여 티켓 판매 플랫폼 인 마이크로 서비스 시스템을 구현하고 있습니다. 시나리오에는 zuul 프록시, eureka 레지스트리 및 3 개의 서비스, 즉 사용자 서비스, 주문 서비스 및 티켓 서비스가 있습니다. 서비스는 feign 선언적 REST 클라이언트를 사용하여 서로 통신합니다.스프링 클라우드 아키텍트를 기반으로 hystrix fallback으로 분산 트랜잭션을 구현하는 방법

지금 티켓을 구입하는 기능이 주요 과정은 다음과 같습니다 :
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); 
    } 
} 

그런는 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. fallback 기능, OrderResource.create(order) 방법 @HystrixCommand 사용.
  2. 생성시 오류가 발생하는 경우 인스턴스가 OrderResource.create(order)에서 사용되며 대체 기능으로 다시 사용됩니다. 이 지속성은 order이지만 롤백됩니다. 그러나이 인스턴스의 데이터는 계속 실행을 확인하는 데 사용될 수 있습니다.
  3. 그래서 어떤 서비스 요청이 있는지 확인하기 위해 'PENDING', 'PAID', 'FINISHED'상태를 사용합니다.
  4. ticketCompositeServiceuserCompositeService은 거짓 클라이언트입니다. feign 클라이언트 메소드 payForOrder()의 경우 fallback을위한 다른 메소드 payForOrderFallback()이 있습니다.
  5. 폴백 메서드를 여러 번 호출 할 수 있어야합니다.
  6. ticketCompositeServiceuserCompositeService 전화에 try/catch을 추가하면 주문이 'FAILED'상태로 저장됩니다.

이 솔루션은 대부분의 경우 작동 할 수 있습니다. 단, 대체 기능에서 userCompositeService.payForOrderFallback(payDTO);에 오류가 있으면 다음 복합 서비스 호출이 호출되지 않습니다.

또 다른 문제는 너무 복잡하다고 생각합니다.

그래서이 시나리오에서 어떻게 dist 트랜잭션을 적절하고 효과적으로 구현해야합니까? 어떤 제안이나 조언이 도움이 될 것입니다. 감사.

+0

Event Sourcing + CQRS 기술을 시도해야합니다 (https://stackoverflow.com/questions/44114755/how-to-do-2-phase-commit-between-two-micro-servicesspring-boot) – sathees

답변

1

Hystrix fallback 내의 보상 로직을 작성하는 것은 지속성이 없기 때문에 위험합니다.

이 접근법은 복원력을 제공하지 않습니다. 데이터베이스의 ACID 보장은 외부 당사자가 관련되어 있기 때문에 여기서는 충분하지 않으며 Hystrix fallback은 코드의 일부가 아닌 것을 막지 않습니다.

예를 들어 지불 완료 후 솔루션에 정전 (예 : 정전 또는 간단한 kill -9)이 발생하면 주문과 보상 로직이 모두 손실되므로 주문이 지불되지만 데이터베이스에는 없습니다. .

보다 탄력적 인 접근 방법은 이벤트 기반 전달을위한 모든 인기있는 메시지 브로커와 처리 논리의 일부 중복 제거가 포함되어 정전 후 이벤트가 다시 전달 될 때 정확히 한 번 서비스 품질을 보장하는 것입니다.

+0

고마워, 나는 또한 많은 테스트 코드를 작성, 이것에 대해 조사, 내 마지막 솔루션은 또한 일부 MQ 및 이벤트 구동 프로세스를 사용하는 것입니다. 감사. – Mavlarn