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:
- Korzystanie
@HystrixCommand
wOrderResource.create(order)
metodzie zfallback
funkcji. - Jeśli wystąpi błąd podczas tworzenia, instancja
order
używana wOrderResource.create(order)
zostanie ponownie użyta jako funkcja zapasowa. Chociaż trwałość tegoorder
zostanie wycofana. Ale dane w tej instancji nadal mogą być wykorzystane do sprawdzenia działania. - Używam więc statusu: "OCZEKIWANIE", "ZAPŁACONE", "ZAKOŃCZONE", aby sprawdzić, czy wykonano pewne zgłoszenie serwisowe.
- i
userCompositeService
to udawany klient. W przypadku metody klienta feignpayForOrder()
dla metody rezerwowej istnieje inna metodapayForOrderFallback()
. - Muszę się upewnić, że metody awaryjne można wywołać wiele razy.
- Dodaję
try/catch
dla połączeńticketCompositeService
iuserCompositeService
, 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.
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