5

Używam chmury wiosennej do wdrożenia mojego systemu mikro usług, platformy sprzedaży biletów. Scenariusz jest taki, że istnieje serwer proxy zuul proxy, rejestr eureka i 3 usługi: usługa użytkownika, usługa zamówienia i usługa biletowa. Usługi korzystają z deklaratywnego klienta REST, aby komunikować się ze sobą.Jak zaimplementować transakcję rozproszoną z awarią hystrix opartą na architekturze Spring Cloud

Teraz jest funkcja kupić bilety, główny proces jest jak poniżej:
1. Służba Porządkowa zaakceptować żądanie utworzenia zamówienie
2. Służba Porządkowa utworzyć podmiot zamówienie z oczekiwaniu status.
3. zamówić usługę serwisową wywołania usługi użytkownika w celu przeprowadzenia płatności użytkownika.
4. zamówić usługę zgłoszeń serwisowych, aby zaktualizować bilety użytkowników.
5. Zlecenie aktualizacji usługi jednostki zamówienia jako ZAKOŃCZONE. Aby uzyskać transakcję, chcę użyć Hystrix Fallback. Na przykład, jeśli proces płatności został zakończony, ale wystąpił błąd podczas ruchu biletu. Jak dowieść płatności użytkownika i statusu zamówienia. Ponieważ płatność użytkownika jest w innej usłudze.

Oto moje obecne rozwiązanie, nie jestem pewien, czy to jest właściwe. Czy jest jakiś inny lepszy sposób to zrobić.

Początkowo 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); 
    } 
} 

Następnie 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; 
    } 
} 

Niektóre kluczowe punkty są tu:

  1. Korzystanie @HystrixCommand w OrderResource.create(order) metodzie z fallback funkcji.
  2. Jeśli wystąpi błąd podczas tworzenia, instancja order używana w OrderResource.create(order) zostanie ponownie użyta jako funkcja zapasowa. Chociaż trwałość tego order zostanie wycofana. Ale dane w tej instancji nadal mogą być wykorzystane do sprawdzenia działania.
  3. Używam więc statusu: "OCZEKIWANIE", "ZAPŁACONE", "ZAKOŃCZONE", aby sprawdzić, czy wykonano pewne zgłoszenie serwisowe.
  4. i userCompositeService to udawany klient. W przypadku metody klienta feign payForOrder() dla metody rezerwowej istnieje inna metoda payForOrderFallback().
  5. Muszę się upewnić, że metody awaryjne można wywołać wiele razy.
  6. Dodaję try/catch dla połączeń ticketCompositeService i userCompositeService, aby upewnić się, że zamówienie zostanie mimo to zapisane ze statusem "NIEPRAWIDŁOWE".

Wygląda na to, że to rozwiązanie może działać w większości przypadków. Z wyjątkiem tego, że w funkcji rezerwowej, jeśli wystąpi jakiś błąd w userCompositeService.payForOrderFallback(payDTO);, to następujące złożone zgłoszenie serwisowe nie zostanie wywołane.

I innym problemem jest, myślę, że to zbyt skomplikowane.

Tak więc, w tym scenariuszu, w jaki sposób należy wdrożyć transakcję dist odpowiednio i skutecznie. Pomocne będą wszelkie sugestie lub porady. Dzięki.

+0

Powinieneś wypróbować techniki Event Sourcing + CQRS [link] (https://stackoverflow.com/questions/44114755/how-to-do-2-phase-commit-between-two-micro-servicesspring-boot) – sathees

Odpowiedz

1

Pisanie logiki kompensacji w ramach funkcji zastępczej Hystrix jest niebezpieczne z powodu braku trwałości.

Takie podejście nie zapewnia żadnej odporności. Gwarancja ACID z bazy danych nie jest tutaj wystarczająca z powodu zaangażowanych stron zewnętrznych, a funkcja zastępcza Hystrix nie chroni cię przed niczym, co nie jest częścią twojego kodu.

Na przykład, jeśli Twoje rozwiązanie zakończy się niepowodzeniem (np. Zanikiem zasilania lub prostym kill -9) po zakończeniu płatności, stracisz zarówno kolejność, jak i logikę rekompensaty, co oznacza, że ​​zamówienie zostanie opłacone, ale nie będzie dostępne w bazie danych .

Bardziej odporne podejście wiązałoby się z każdym popularnym brokerem wiadomości w przypadku dostarczania zdarzeń i pewną deduplikacją w logice przetwarzania w celu zapewnienia dokładnie raz jakości usługi, gdy zdarzenia zostaną ponownie dostarczone po awarii.

+0

dzięki, badam też o tym dużo, pisząc wiele kodu testowego, moim ostatecznym rozwiązaniem jest również wykorzystanie jakiegoś MQ i procesu opartego na zdarzeniach. Dzięki. – Mavlarn