Załóżmy, że mamy funkcję, która aktualizuje hasło użytkownika.
Po kliknięciu przycisku „Aktualizuj hasło”, UpdatePasswordEvent jest wysyłany do tematu, w którym subskrybowane są 3 inne usługi:
- Usługa, która faktycznie aktualizuje hasło użytkownika
- Usługa aktualizująca historię haseł użytkownika
- Usługa, która wysyła wiadomość e-mail informującą użytkownika, że jego hasło zostało zmienione.
W oparciu o to, co zrozumiałem na temat ostatecznej spójności, wszystkie te usługi (konsumenci) otrzymają wydarzenie w tym samym czasie i przetworzą je osobno, co w dobrym scenariuszu doprowadzi do spójności danych.
Co jednak jeśli usługa nie przetworzy zdarzenia? np. nagłe rozłączenie, błąd bazy danych itp. Jaki jest dobry wzorzec / praktyka obsługi tych błędów transakcji?
Myślałem o stworzeniu RollbackTopic, w którym jeśli jakiekolwiek zdarzenie nie zostanie przetworzone, RollbackEvent zostanie utworzony w temacie, w którym „usługi wycofania” wykonają swoje zadanie i przywrócą dane z powrotem
Odpowiedzi:
Nie, niekoniecznie. Jak skomentowałem, nie możemy cofnąć wysłanego e-maila, więc nadal potrzebujemy czegoś w rodzaju „sekwencji”. IPC w zakresie zarządzania danymi opartymi na zdarzeniach nie jest zwolnione z organizacji 1 .
Na przykład wiadomość e-mail nie powinna zostać wysłana, chyba że poprzednie transakcje zakończą się powodzeniem, a usługa e-mail otrzyma dowód. 3)
Przywitaj się z błędami obliczeń rozproszonych . To właśnie komplikują sprawy i, jak zwykle, nie ma srebrnych kul, z którymi można by sobie poradzić.
Przed wyruszeniem w podróż w poszukiwaniu Zaginionej Arki musimy najpierw zapytać organizację. Często rozwiązaniem jest sposób, w jaki organizacja boryka się z tymi problemami w prawdziwym świecie .
Co robią wszyscy (działy), gdy brakuje niektórych danych lub są one niekompletne?
Przekonamy się, że różne działy mają różne rozwiązania, które łącznie stanowią rozwiązanie do wdrożenia.
W każdym razie, oto kilka praktyk, które mogą pomóc nam w realizacji strategii.
Ostateczna spójność
Zamiast zapewniać, że system jest cały czas w spójnym stanie, możemy zaakceptować, że system go dostanie w pewnym momencie w przyszłości. Takie podejście jest szczególnie przydatne w przypadku długotrwałych operacji biznesowych.
Sposób, w jaki system może osiągnąć spójność, różni się w zależności od systemu. Może obejmować od zautomatyzowanych procesów do pewnego rodzaju interwencji człowieka. Na przykład typowe próbowanie go ponownie później lub kontakt z obsługą klienta .
Przerwij wszystkie operacje
Przywróć system do spójnego stanu dzięki kompensacji transakcji . Musimy jednak wziąć pod uwagę, że transakcje te mogą również zawieść, co może doprowadzić nas do punktu, w którym niespójność jest jeszcze trudniejsza do rozwiązania. I znowu nie możemy cofnąć wysłanego e-maila.
W przypadku małej liczby transakcji takie podejście jest wykonalne, ponieważ liczba transakcji kompensujących jest również niska. Gdyby w IPC było zaangażowanych kilka transakcji biznesowych, obsługa jednej transakcji kompensacyjnej dla każdej z nich byłaby trudna.
Jeśli pójdziemy na transakcje kompensacyjne , okaże się, że wzorzec projektowy wyłącznika jest bardzo przydatny - i obowiązkowo ośmielę się powiedzieć -
Transakcje rozproszone
Pomysł polega na objęciu wielu transakcji w ramach jednej transakcji, poprzez ogólny proces zarządzania znany jako Transaction Manager . Częstym algorytmem do obsługi transakcji rozproszonych jest zatwierdzanie dwufazowe .
Główną troską rozproszonych transakcji jest to, że polegają one na blokowaniu zasobów w trakcie ich trwania, a jak wiemy, w przypadku Menedżera transakcji może się również nie udać .
Jeśli menedżerowie transakcji zostaną narażeni na szwank, możemy skończyć z kilkoma blokadami w różnych kontekstach ograniczonych, co skutkuje nieoczekiwanymi zachowaniami wynikającymi z kolejkowania wiadomości. 2)
Rozkład operacji. Czemu?
Zgodnie z powyższymi argumentami Sam - w swojej książce Building Microservices - stwierdza, że jeśli naprawdę nie możemy sobie pozwolić na ostateczną spójność, powinniśmy teraz unikać podziału operacji.
Jeśli nie możemy sobie pozwolić na podział niektórych operacji na dwie lub więcej transakcji, można powiedzieć, że - prawdopodobnie - transakcje te należą do tego samego ograniczonego kontekstu, a przynajmniej do kontekstu przekrojowego, który pozostaje do modelowania.
Na przykład w naszym przypadku zdajemy sobie sprawę, że transakcje nr 1 i nr 2 są ze sobą ściśle powiązane i prawdopodobnie oba mogą należeć do tego samego ograniczonego kontekstu Konta , Użytkownicy , Rejestr , cokolwiek ...
Rozważ umieszczenie obu operacji w granicach tej samej transakcji. Ułatwi to obsługę całej operacji. Waży również poziom krytyczności każdej transakcji. Prawdopodobnie, jeśli transakcja nr 2 zakończy się niepowodzeniem, nie powinno to zagrozić całej operacji. W razie wątpliwości zapytaj organizację .
1: Nie taki rodzaj aranżacji, jaki myślisz. Nie mówię o orkiestrze ESB. Mówię o tym, aby usługi reagowały na właściwe wydarzenie.
2: Możesz znaleźć ciekawe opinie Sama Newmana dotyczące transakcji rozproszonych.
3: Sprawdź odpowiedź Davida Parkera na ten temat.
źródło
W twoim przypadku nie możesz po prostu przetworzyć wszystkich trzech rzeczy naraz. Potrzebujesz procesu. Oto niezwykle uproszczony przykład:
Ważne jest, aby wiedzieć, że operacje zmieniające stan MUSZĄ być zawsze wykonywane na spójnej jednostce. O ile nie można zagwarantować silnej spójności , należy to zrobić na podstawie rekordu głównego.
Twój system powinien zagwarantować, że zanim jakiekolwiek zdarzenie zostanie zgłoszone w zmianach w systemie, MUSI być najpierw utrwalony z bezpieczeństwem transakcyjnym. Ma to na celu zapewnienie, że podniesione wydarzenie naprawdę jest potwierdzeniem tego, co się naprawdę wydarzyło.
Jest kilka trudnych części tego procesu i zamierzam zignorować te oczywiste - na przykład: Co się stanie, jeśli serwer bazy danych umrze, gdy utrzyma użytkownika ze zmienionym hasłem? Po prostu ponownie wydajesz UpdatePassword. Należy jednak zająć się niektórymi częściami, a są to:
W systemie orkiestrator procesów (PO) jest niczym innym jak innym bytem, który zawiera stan wewnętrzny - również w dosłownym znaczeniu - i umożliwia przechodzenie między stanami, skutecznie działając jako pewnego rodzaju maszyna stanu. Dzięki wewnętrznemu stanowi możesz usunąć przetwarzanie duplikacji wiadomości.
Kiedy PO jest w
New
stanie i przetwarzaUserPasswordHasBeenUpdated
, zmienia swój stan naUserPasswordHasBeenUpdated
(lub inną nazwę stanu, która Ci odpowiada). Gdyby zamówienie nadal znajdowało się na liście,UserPasswordHasBeenUpdated
a innyUserPasswordHasBeenUpdated
nadejdzie, zamówienie to całkowicie zignoruje wiadomość, wiedząc, że jest to powielanie. Podobny mechanizm zostałby wdrożony również dla innych państw.Obsługa faktycznego wysyłania wiadomości e-mail jest nieco trudniejsza. Tutaj masz dwie opcje:
Wyślij to co najwyżej raz
Dzięki tej opcji, gdy PO osiągnie
UserPasswordHistoryHasBeenSaved
stan, polecenie wysłania wiadomości e-mail jest wysyłane jako reakcja na zmianę stanu. Twój system upewni się, żeUserPasswordHistoryHasBeenSaved
stan zostanie utrwalony przed wysłaniem wiadomości e-mail, tzn. Zduplikowana wiadomość nie wyzwoli ponownego wysłania wiadomości e-mail. Dzięki takiemu podejściu masz pewność, że zachowany jest właściwy stan zamówienia zakupu, ale nie możesz zagwarantować żadnej kolejnej operacji.Wyślij co najmniej raz
Po to bym poszedł.
Zamiast zapisywać
UserPasswordHistoryHasBeenSaved
i wysyłać wiadomości e-mail jako reakcję, najpierw próbujesz wysłać wiadomość e-mail. Jeśli operacja wysyłania zakończy się niepowodzeniem, stan zamówienia zakupu nigdy nie jest zmieniany na,UserPasswordHistoryHasBeenSaved
a inna wiadomość tego samego typu jest nadal przetwarzana. Jeśli wysłanie wiadomości e-mail rzeczywiście się powiedzie, ale system zawiedzie podczas utrzymywania zamówienia zakupu w nowymUserPasswordHistoryHasBeenSaved
stanie, kolejna wiadomośćUserPasswordHistoryHasBeenSaved
wysłałaby ponownie polecenie wysłania wiadomości e-mail, a użytkownik otrzymałby ją wiele razy .W twoim przypadku chcesz się upewnić, że użytkownik faktycznie otrzyma wiadomość e-mail. Dlatego wybrałbym drugą opcję zamiast pierwszej.
źródło
Systemy kolejkowania nie są tak delikatne, jak mogłoby się wydawać.
Gdybyśmy pisali wszystkie trzy procesy do relacyjnej bazy danych, moglibyśmy użyć transakcji do obsługi awarii procesów w połowie.
Bez ostatecznego zatwierdzenia częściowa praca zostałaby odrzucona.
W systemie bazowym kolejki będziesz mieć podobne opcje, kiedy czytasz wiadomość z kolejki, aby obsłużyć awarie w trakcie procesu.
Na przykład Amazon SQS po prostu ukrywa czytane wiadomości. jeśli nie zostanie wysłane ostatnie polecenie Usuń, wiadomość pojawi się ponownie lub zostanie umieszczona w kolejce niedostarczonych wiadomości.
Możesz realizować podobne „transakcje” na różne sposoby, zasadniczo przechowując kopię wiadomości, dopóki nie otrzymasz potwierdzenia pomyślnego przetworzenia. Jeśli potwierdzenie nie zostanie odebrane na czas. możesz ponownie wysłać wiadomość lub zachować ją do ręcznej uwagi.
Potencjalnie możesz stworzyć „usługę wycofywania”, która monitorowałaby te błędne wiadomości, wiedziała o wiadomościach pokrewnych i przeszłym stanie oraz przeprowadzała wycofywanie.
Jednak! Zwykle lepiej jest po prostu ponownie wysłać błędne wiadomości. W końcu są to zwykle przypadki krawędziowe. Albo serwer uległ katastrofalnej awarii, albo wystąpił błąd w obsłudze określonego typu wiadomości.
Po otrzymaniu powiadomienia o błędzie usługa może zostać naprawiona, a wiadomości przetworzone pomyślnie. Przywracanie systemu jako całości do spójnego stanu.
źródło
Stajesz przed problemem dwóch generałów . Zasadniczo: w jaki sposób możesz mieć pewność, że wiadomość zostanie odebrana i nastąpi odpowiedź na tę wiadomość? W wielu przypadkach idealne rozwiązanie nie istnieje. W rzeczywistości w systemie rozproszonym często niemożliwe jest uzyskanie dostawy wiadomości dokładnie raz .
Pierwszą oczywistą uwagą jest to, że usługa zmieniająca hasło powinna wysyłać zdarzenie zmiany hasła. W ten sposób historia haseł i usługi wysyłania poczty są uruchamiane tylko wtedy, gdy hasło faktycznie się zmienia, niezależnie od tego, dlaczego zostało zmienione.
Aby faktycznie rozwiązać problem, nie rozważyłbym transakcji rozproszonych, ale zamiast tego spojrzałem w kierunku dostarczenia wiadomości przynajmniej raz i idempotentnego przetwarzania.
Przynajmniej raz
Aby upewnić się, że zdarzenie zmiany hasła jest rzeczywiście widoczne dla wszystkich konsumentów, musisz użyć trwałego kanału komunikacyjnego, w którym wiadomości mogą być odbierane w stylu „przynajmniej raz”. Konsumenci uznają wiadomość za zużytą tylko wtedy, gdy w pełni ją przetworzyli. Jeśli na przykład usługa historii haseł ulegnie awarii podczas zapisywania pozycji historii, po ponownym uruchomieniu ponownie odczyta to samo zdarzenie zmiany hasła i spróbuje ponownie, potwierdzając to zdarzenie jako tylko do odczytu po zapisaniu się w historii. Powinieneś wybrać rozwiązanie kolejki wiadomości w oparciu o jego zdolność do ponownego wysyłania wiadomości, dopóki nie zostaną potwierdzone.
Idempotencja
Po osiągnięciu przynajmniej raz dostawy pojawia się problem zdublowanych działań, gdy wiadomość została częściowo przetworzona przed przerwaniem konsumenta, a następnie ponownym przetworzeniem. Należy to rozwiązać, projektując każdą usługę, aby była idempotentna. Albo zapisy, które wykonuje, mogą wystąpić wiele razy bez negatywnych skutków, albo zachowuje swój własny zbiór podejmowanych działań i unika wykonywania akcji więcej niż raz. W przypadku wysyłania poczty prawdopodobnie nie warto próbować sprawić, by zachowywała się idempotentnie i po prostu dobrze, że czasami poczta jest wysyłana dwukrotnie.
W każdym razie uważaj, jak mikro tworzysz swoje usługi. Czy usługa historii haseł naprawdę musi być niezależna od usługi zmiany hasła?
źródło
Nie zgadzam się z wieloma odpowiedziami.
Istnieją inne obietnice spójności, które można dodać.
Te dodatkowe spójności będą musiały zostać wdrożone w zależności od czynów aplikacji.
Nie mam pojęcia, co masz na myśli przez „aktualizowanie historii”, ale proszę, nigdy nie zmieniaj historii. Jeśli tylko rozszerzasz DAG, powinno to spowodować zmianę w bieżącym stanie. Nie są niezależni. Jeśli tak, to nie możesz polegać na historii odzwierciedlającej to, co się stało. (i na koniec, nie przechowuj haseł, zobacz, jak nie przechowywać haseł )
źródło
consider asking the organization first.
. Prawdopodobnie masz rację. Uważam jednak, że ważne jest uwarunkowanie zdarzeń, których nie możemy cofnąć. Na przykład powiadomienia dla użytkownika końcowego. Powiadomienie leżące na rzeczywistym stanie danych użytkownika może powodować złe wrażenie.