Jednym z głównych problemów, które widziałem w systemie z mikrousługami, jest sposób działania transakcji, gdy obejmują one różne usługi. W ramach naszej własnej architektury korzystamy z transakcji rozproszonych, aby rozwiązać ten problem, ale wiążą się one z własnymi problemami. Szczególnie impasy były jak dotąd bólem.
Kolejną opcją wydaje się być niestandardowy menedżer transakcji, który zna przepływy w twoim systemie i zajmie się wycofaniem zmian jako procesem w tle obejmującym cały system (więc powie innym usługom o wycofaniu a jeśli są wyłączone, powiadom ich później).
Czy istnieje inna akceptowana opcja? Oba wydają się mieć swoje wady. Pierwszy z nich może powodować impasy i wiele innych problemów, drugi może powodować niespójność danych. Czy są lepsze opcje?
źródło
Odpowiedzi:
Zazwyczaj stosuje się izolację mikrousług w jak największym stopniu - należy traktować je jako pojedyncze jednostki. Następnie transakcje mogą być opracowywane w kontekście usługi jako całości (tj. Nie stanowią części zwykłych transakcji DB, chociaż nadal możesz mieć transakcje DB wewnątrz usługi).
Zastanów się, w jaki sposób występują transakcje i jakie to ma sens dla twoich usług, możesz wdrożyć mechanizm wycofywania, który nie wykonuje pierwotnej operacji, lub dwufazowy system zatwierdzania, który rezerwuje pierwotną operację, dopóki nie zostanie nakazany, aby zatwierdzić na prawdziwe. Oczywiście oba te systemy oznaczają, że wdrażasz własny, ale już wdrażasz mikrousługi.
Usługi finansowe robią takie rzeczy przez cały czas - jeśli chcę przenieść pieniądze z mojego banku do twojego banku, nie ma jednej transakcji takiej jak w DB. Nie wiesz, jakie systemy działają w obu bankach, więc musisz skutecznie traktować każdy z nich jak swoje mikrousługi. W takim przypadku mój bank przeniósłby moje pieniądze z mojego konta na rachunek posiadania, a następnie powiedziałby bankowi, że mają trochę pieniędzy. Jeśli to nie powiedzie się, mój bank zwróci moje konto środkami, które próbował wysłać.
źródło
Myślę, że standardową mądrością jest, aby transakcje nigdy nie przekraczały granic mikrousług. Jeśli jakikolwiek zestaw danych naprawdę musi być atomowo spójny z innym, te dwie rzeczy należą do siebie.
Jest to jeden z powodów, dla których bardzo trudno jest podzielić system na usługi, dopóki nie zostanie w pełni zaprojektowany. Co we współczesnym świecie prawdopodobnie oznacza napisane ...
źródło
Myślę, że jeśli spójność jest silnym wymogiem w twojej aplikacji, powinieneś zadać sobie pytanie, czy mikrousług jest lepszym podejściem. Jak mówi Martin Fowler :
Ale może w twoim przypadku możesz poświęcić spójność na rzecz dostępności
Jednak zadaję sobie również pytanie, czy istnieje strategia dla rozproszonych transakcji w mikrousługach, ale być może koszt jest zbyt wysoki. Chciałem dać wam moje dwa centy z zawsze doskonałym artykułem Martina Fowlera i twierdzeniem CAP .
źródło
Jak zasugerowano w co najmniej jednej z odpowiedzi tutaj, ale także w innych miejscach w sieci, możliwe jest zaprojektowanie jednej mikrousługi, która utrzymuje jednostki razem w ramach normalnej transakcji, jeśli potrzebujesz spójności między tymi dwiema jednostkami.
Ale jednocześnie może się zdarzyć, że podmioty tak naprawdę nie należą do tej samej mikrousługi, na przykład ewidencji sprzedaży i ewidencji zamówień (kiedy zamawia się coś, aby zrealizować sprzedaż). W takich przypadkach możesz potrzebować sposobu zapewnienia spójności między dwiema mikrousługami.
Stosowano tradycyjnie rozproszone transakcje i według mojego doświadczenia działają one dobrze, dopóki nie zostaną skalowane do rozmiaru, w którym blokowanie staje się problemem. Możesz rozluźnić blokadę, aby tak naprawdę tylko odpowiednie zasoby (np. Sprzedawany przedmiot) były „blokowane” za pomocą zmiany stanu, ale w tym momencie zaczyna się robić trudniej, ponieważ wchodzisz na terytorium, na którym musisz zbudować wszystkie logika, aby zrobić to sam, zamiast powiedzieć, że baza danych sobie z tym poradzi.
Współpracowałem z firmami, które poszły drogą budowania własnych ram transakcyjnych do obsługi tego złożonego problemu, ale nie polecam go, ponieważ jest drogi i wymaga dojrzałości.
Istnieją produkty, które można przykręcić do systemu i które zapewniają spójność. Dobrym przykładem jest silnik procesów biznesowych, który zazwyczaj radzi sobie z konsekwencją ostatecznie i za pomocą kompensacji. Inne produkty działają w podobny sposób. Zazwyczaj kończy się warstwa oprogramowania w pobliżu klienta (ów), która zajmuje się spójnością oraz usługami (mikro) transakcji i połączeń w celu przeprowadzenia rzeczywistego przetwarzania biznesowego . Jednym z takich produktów jest ogólne złącze JCA, którego można używać z rozwiązaniami Java EE (dla przejrzystości: jestem autorem). Zobacz http://blog.maxant.co.uk/pebble/2015/08/04/1438716480000.html, aby uzyskać więcej szczegółów i głębsze omówienie poruszonych tutaj problemów.
Innym sposobem obsługi transakcji i spójności jest zawinięcie połączenia z mikrousługą w połączenie z czymś transakcyjnym, takim jak kolejka komunikatów. Weź powyższy przykład rekordu sprzedaży / zamówienia - możesz po prostu pozwolić mikrousługi sprzedaży wysłać komunikat do systemu zamówień, który jest zatwierdzony w tej samej transakcji, która zapisuje sprzedaż do bazy danych. Rezultatem jest rozwiązanie asynchroniczne, które skaluje się bardzo dobrze. Korzystając z technologii takich jak gniazda sieciowe, można nawet obejść problem blokowania, który często wiąże się ze skalowaniem rozwiązań asynchronicznych. Aby uzyskać więcej pomysłów na takie wzory, zobacz inny z moich artykułów: http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html .
Niezależnie od tego, które rozwiązanie wybierzesz, ważne jest, aby pamiętać, że tylko niewielka część twojego systemu będzie pisać rzeczy, które muszą być spójne - większość dostępu będzie prawdopodobnie tylko do odczytu. Z tego powodu wbuduj zarządzanie transakcjami tylko w odpowiednie części systemu, aby nadal można było dobrze skalować.
źródło
W mikrousługach istnieją trzy sposoby osiągnięcia spójności między różnicami. usługi:
Orchestration - Jeden proces zarządzający transakcją i wycofywaniem usług.
Choreografia - usługa przekazuje komunikaty między sobą i ostatecznie osiąga spójny stan.
Hybrydowy - mieszanie dwóch powyższych.
Aby przeczytać całość, przejdź do linku: https://medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c
źródło
Istnieje wiele rozwiązań, które kompromisują bardziej, niż czuję się swobodnie. To prawda, że jeśli twoja sprawa użycia jest złożona, na przykład przenoszenie pieniędzy między różnymi bankami, przyjemniejsze alternatywy mogą być niemożliwe. Ale spójrzmy na to, co możemy zrobić we wspólnym scenariuszu, w którym użycie mikrousług zakłóca nasze przyszłe transakcje w bazie danych.
Opcja 1: Jeśli to możliwe, unikaj potrzeby transakcji
Oczywiste i wspomniane wcześniej, ale idealne, jeśli damy radę. Czy komponenty faktycznie należały do tej samej mikrousługi? Czy możemy przeprojektować system (systemy) tak, aby transakcja stała się niepotrzebna? Być może akceptacja braku transakcji jest najbardziej przystępną ofiarą.
Opcja 2: Użyj kolejki
Jeśli istnieje wystarczająca pewność, że druga usługa odniesie sukces we wszystkim, co chcemy, możemy wywołać ją za pomocą jakiejś formy kolejki. Element w kolejce nie zostanie odebrany do później, ale możemy upewnić się, że element jest w kolejce .
Powiedzmy na przykład, że chcemy wstawić podmiot i wysłać wiadomość e-mail jako pojedynczą transakcję. Zamiast dzwonić na serwer pocztowy, umieszczamy wiadomość w kolejce w tabeli.
Oczywistą wadą jest to, że wiele mikrousług będzie potrzebowało dostępu do tej samej tabeli.
Opcja 3: Wykonaj zewnętrzną pracę, tuż przed sfinalizowaniem transakcji
Podejście to opiera się na założeniu, że popełnienie transakcji jest bardzo mało prawdopodobne.
Jeśli zapytania nie powiodą się, połączenie zewnętrzne jeszcze się nie odbyło. Jeśli połączenie zewnętrzne nie powiedzie się, transakcja nigdy nie zostanie zatwierdzona.
Takie podejście wiąże się z ograniczeniami polegającymi na tym, że możemy wykonać tylko jedno połączenie zewnętrzne i musi być wykonane na końcu (tzn. Nie możemy wykorzystać jego wyniku w naszych zapytaniach).
Opcja 4: Twórz rzeczy w stanie oczekiwania
Jak opublikowano tutaj , możemy mieć wiele mikrousług tworzących różne komponenty, każdy w stanie oczekiwania, bez transakcji.
Sprawdzanie poprawności jest wykonywane, ale nic nie jest tworzone w stanie ostatecznym. Po pomyślnym utworzeniu wszystkiego każdy komponent jest aktywowany. Zwykle ta operacja jest tak prosta, a prawdopodobieństwo, że coś pójdzie nie tak, jest tak małe, że możemy nawet zdecydować się na aktywację bez transakcji.
Największą wadą jest prawdopodobnie to, że musimy uwzględnić istnienie oczekujących przedmiotów. Każde wybrane zapytanie musi rozważyć, czy dołączyć oczekujące dane. Większość powinna to zignorować. A aktualizacje to zupełnie inna historia.
Opcja 5: Pozwól mikroserwisowi udostępnić swoje zapytanie
Żadna z pozostałych opcji tego nie robi dla Ciebie? Zatem bądźmy niekonwencjonalni .
W zależności od firmy ta może być nie do przyjęcia. Jestem świadomy. To jest niekonwencjonalne. Jeśli to nie do przyjęcia, wybierz inną trasę. Ale jeśli pasuje to do Twojej sytuacji, rozwiązuje problem prosto i skutecznie. To może być najbardziej akceptowalny kompromis.
Istnieje sposób na przekształcenie zapytań z wielu mikrousług w prostą, pojedynczą transakcję bazy danych.
Zwraca zapytanie zamiast go wykonywać.
Jeśli chodzi o sieć, każda mikrousługa musi mieć dostęp do każdej bazy danych. Należy o tym pamiętać, również w odniesieniu do skalowania w przyszłości.
Jeśli bazy danych biorące udział w transakcji znajdują się na tym samym serwerze, będzie to zwykła transakcja. Jeśli są na różnych serwerach, będzie to transakcja rozproszona. Kod jest taki sam, niezależnie od tego.
Otrzymujemy zapytanie, w tym jego typ połączenia, parametry i ciąg połączenia. Możemy zawinąć je w czystą, wykonalną klasę Command, dzięki czemu przepływ będzie czytelny: Wywołanie mikrousług powoduje powstanie polecenia, które wykonujemy w ramach naszej transakcji.
Ciąg połączenia jest tym, co daje nam inicjująca mikrousługa, więc dla wszystkich celów i celów kwerenda jest nadal uważana za wykonaną przez tę mikrousługę. Po prostu fizycznie kierujemy go przez mikrousługę klienta. Czy to robi różnicę? Pozwala nam to umieścić tę samą transakcję z innym zapytaniem.
Jeśli kompromis jest akceptowalny, to podejście daje nam prostą transakcyjność aplikacji monolitowej w architekturze mikrousługowej.
źródło
Zacznę od dekompozycji przestrzeni problemowej - identyfikacji granic usług . Po prawidłowym wykonaniu nigdy nie będziesz musiał zawierać transakcji między usługami.
Różne usługi mają własne dane, zachowanie, siły motywacyjne, rząd, reguły biznesowe itp. Na dobry początek należy wymienić możliwości wysokiego poziomu, jakie posiada przedsiębiorstwo. Na przykład marketing, sprzedaż, księgowość, wsparcie. Kolejnym punktem wyjścia jest struktura organizacyjna, ale należy pamiętać, że istnieje pewne zastrzeżenie - z pewnych powodów (na przykład politycznych) może to nie być optymalny schemat dekompozycji przedsiębiorstw. Bardziej rygorystycznym podejściem jest analiza łańcucha wartości . Pamiętaj, że twoje usługi mogą obejmować również ludzi, nie jest to wyłącznie oprogramowanie. Usługi powinny komunikować się ze sobą za pośrednictwem wydarzeń .
Następnym krokiem jest rzeźbienie tych usług. W rezultacie otrzymujesz względnie niezależne agregaty . Reprezentują one jednostkę spójności. Innymi słowy, ich elementy wewnętrzne powinny być spójne i ACID. Agregaty komunikują się także ze sobą poprzez zdarzenia.
Jeśli uważasz, że Twoja domena wymaga najpierw spójności, pomyśl jeszcze raz. Żaden z dużych i krytycznych systemów nie został zbudowany z myślą o tym. Wszystkie są rozproszone i ostatecznie spójne. Sprawdź klasyczny papier Pat Helland .
Oto kilka praktycznych wskazówek na temat budowy systemu rozproszonego.
źródło