Obsługa zmian w architekturze mikrousług opartej na zdarzeniach

9

Robię projekt badawczy, w którym badam opcje obsługi zmian w architekturze mikrousług opartej na zdarzeniach.

Powiedzmy, że mamy aplikację, w której mamy cztery różne usługi. Każda z tych usług ma własną bazę danych do przechowywania danych lokalnych.

W tej konfiguracji cztery usługi komunikują się ze sobą za pomocą magistrali zdarzeń. Kiedy więc coś się dzieje w serwisie, publikuje wydarzenie. Wszystkie inne usługi zainteresowane tym wydarzeniem przetwarzają je na swój własny sposób.

W takim przypadku różne usługi w architekturze muszą mieć „kontrakty” dotyczące treści tych zdarzeń (atrybuty itp.). Usługi mają więc „luźno powiązane zależności” od tych zdarzeń

Moje pytanie brzmi: jak poradzić sobie ze zmianami tych wydarzeń?

Powiedzmy, że usługa A rejestruje nowych użytkowników w aplikacji. Wysyła więc zdarzenie „Zarejestrowane przez użytkownika”. Usługa B odbiera to zdarzenie i przetwarza je. Jednak niektórzy programiści z zespołu usługi C zdecydowali, że potrzebują również płci zarejestrowanego użytkownika. Zdarzenie zostało zmienione, a atrybut płci jest dodawany do zdarzenia „UserRegistered”.

W jaki sposób możemy upewnić się, że usługa B może nadal odbierać to samo zdarzenie z tym dodatkowym atrybutem bez ponownego wdrażania?

Czy istnieją inne sposoby podejścia do tego problemu niż wersjonowanie tych zdarzeń?

CGeense
źródło
W jakim formacie są twoje wiadomości, czy jest to coś, co możesz zaprojektować? Niektóre formaty wiadomości dopuszczają opcjonalne atrybuty. W zależności od implementacji czytnika można dodawać opcjonalne atrybuty bez konieczności aktualizacji wszystkich czytników.
Thomas Owens
Jestem wolny w wyborze formatu dla moich wiadomości. Myślę, że najlepszym rozwiązaniem jest używanie JSON. Ważne jest, aby te różne usługi były zbudowane w różnych językach. Dlatego potrzebny jest ogólny format, taki jak XML lub JSON.
CGeense,

Odpowiedzi:

1

Wydarzenia nie dotyczą tego, co się zmieniło. Chodzi o to, kiedy coś się zmieniło.

Mogę stworzyć system zdarzeń całkowicie oddzielony od zawartości, która się zmieniła. W ten sposób uczę się ze zdarzenia, że ​​obiekt został zaktualizowany. Jeśli zależy mi nawet na tym, że obiekt został zaktualizowany, powiem cokolwiek, jak się z nim rozmawia, aby zapytać, co się zmieniło.

To nie rozwiązuje problemu komunikowania tych zmian. To po prostu powstrzymuje go przed wejściem do systemu wydarzeń.

Przykładem jednego ze sposobów rozwiązania problemu różnych wersji danych jest skłonienie obserwatora do utworzenia i przekazania obserwowanego obiektu kolekcji. Obserwowany obiekt zapełnia kolekcję najnowszymi danymi, a gdy kontrola zwróci Ci (obserwator), masz to, czego potrzebujesz. Jeśli jest coś dodatkowego, na czym ci nie zależy, ponieważ nigdy o nim nie słyszałeś, po prostu go ignorujesz.

Wiele innych sposobów na skórowanie tego kota, ale właśnie w tym przypadku zrobiłem pracę.

candied_orange
źródło
Czy nie zwiększyłoby to radykalnie ruchu między usługami? Korzystając z przykładu w pytaniu, UserRegisteredzdarzenie, jeśli wystąpi zdarzenie, które nie zawiera informacji o użytkowniku, zostanie wysłana 1 wiadomość do magistrali, a następnie {liczba zainteresowanych usług} żądań do usługi użytkownika lub opublikowanych wiadomości do autobusu. Wtedy pojawiłaby się {liczba zainteresowanych usług} wiadomości o różnych rozmiarach. Chociaż uważam, że jest to prawdopodobnie czystsza konstrukcja na papierze, jeśli wydajność jest problemem, to psuje się w każdym nietrywialnym systemie, szczególnie w sieci.
Thomas Owens
@ThomasOwens Wysyłanie danych ze zdarzeniem oznacza, że ​​jeśli mam N obserwatorów, wysyłam N wiadomości. Samo wysłanie zdarzenia oznacza, że ​​wysyłam 3N wiadomości, z których tylko 1 zawiera pakiet danych. To dobrze skaluje się nawet przez sieć. Jedynym znaczącym minusem jest potrojenie twojego opóźnienia. Nie oznacza to, że nie można znaleźć bardziej optymalnego rozwiązania dla konkretnej sytuacji. Pokazuję, że systemy zdarzeń i wersje danych nie muszą być ze sobą powiązane.
candied_orange
1
Ideą tego autobusu eventowego jest rozdzielenie różnych usług. Korzystając z oprogramowania pośredniego, możemy upewnić się, że usługi te się nie znają i mogą istnieć i komunikować się, nie wiedząc o swoim istnieniu. Jeśli usuniemy stan z tych zdarzeń i pozwolimy, aby usługi łączyły się ze sobą bezpośrednio, łączymy te usługi. W ten sposób nigdy nie możemy ponownie
wdrożyć
1
Chodzi o to, że system zdarzeń lub nie potrzebujesz danych rozszerzalnych. JSON lub XML robią to dobrze, jeśli nie zmienisz nazw lub struktur, które zostały wcześniej ustanowione. Zrobiłem to samo z kolekcjami. System wydarzeń nie powinien obchodzić płci. Jeśli wysyła dane, powinien je po prostu przekazać, a coś na drugim końcu albo będzie zależało na płci, albo nie.
candied_orange
1

Frameworki takie jak NServiceBus radzą sobie z tym za pomocą wersjonowania zdarzeń z polimorficzną wysyłką komunikatów.

Na przykład wersja 1 usługi A może publikować zdarzenie jako IUserRegistered_v1. Gdy usługa A w wersji 1.1 musi zawierać dodatkowe pole, może zadeklarować interfejs IUserRegistered_v1_1, który odziedziczy po IUserRegistered_v1, a także zadeklarować dodatkowe pola.

Gdy usługa A opublikuje zdarzenie IUserRegistered_v1_1, NServiceBus wyśle ​​komunikat do wszystkich punktów końcowych, które obsługują IUserRegistered_v1 lub IUserRegistered_v1_1.

pnschofield
źródło
0

Przyrostowa poprawa

Prosta zmiana modelu polega na tym, że kiedy słuchacze rejestrują się jako obserwatorzy, zawierają listę lub inną strukturę elementów danych, o których chcieliby wiedzieć. Może to działać, jeśli dane zwrócone z usługi są proste, ale jeśli masz sporo danych hierarchicznych, wdrożenie może być naprawdę skomplikowane.

Twardy jak skała

Jeśli naprawdę potrzebujesz solidnego sposobu wykonania tego projektu, usługa zachowuje historię zmian, które zostały wprowadzone w przechowywanych danych. Zasadniczo nigdy nie aktualizujesz rekordów w bazie danych, dodajesz nowe rekordy, w których każdy reprezentuje zmianę. Każdy z tych nowych rekordów jest powiązany z identyfikatorem zdarzenia, który identyfikuje akcję. Przechowywany jest parzysty zapis zawierający wszystkie istotne informacje o zmianie (kto, co, kiedy itp.). Ma to inne zalety, które są poza zakresem tej odpowiedzi, ale są omówione w tym artykule na temat twierdzenia CAP .

Po wprowadzeniu zmiany tworzysz rekord zdarzenia i dodajesz wszystkie nowe dane do bazy danych. Następnie publikujesz zdarzenie w detektorach, które zawiera (minimalnie) identyfikator zdarzenia. Słuchacze mogą następnie zażądać danych powiązanych z tym identyfikatorem i uzyskać wersję danych powiązanych z tym identyfikatorem. Każdy słuchacz jest w stanie uzyskać wszystko, czego potrzebuje, bez łączenia potrzeb innych słuchaczy. Radzę dodać podzbiór najczęściej używanych pól danych do komunikatu o zdarzeniu, aby nasłuchiwania mogły odfiltrować zdarzenia, które nie są nimi zainteresowane. Może to zmniejszyć chrapliwość procesu, a niektórzy nasłuchujący mogą nigdy nie musieć dzwonić w ogóle. Chroni to również przed problemami czasowymi. Jeśli po prostu oddzwonisz do usługi i otrzymasz dane na podstawie klucza, mogą wystąpić inne zmiany między uzyskaniem zdarzenia a odzyskaniem danych dla niego. Może to nie mieć znaczenia dla wszystkich słuchaczy, ale może powodować duże problemy, jeśli musisz wiedzieć o wszystkich zmianach. Powyższe stopniowe ulepszenie projektu jest zgodne z tym podejściem, jeśli naprawdę chcesz zmienić go na 11.

Niektóre z nich mogą być przesadą w stosunku do tego, co musisz zrobić, ale z mojego doświadczenia wynika, że ​​jeśli nie masz dokładnego sposobu na sprawdzenie, jak zmienia się zapis w czasie, Ty lub ktoś pracujący z twoimi danymi w końcu będzie tego chciał.

JimmyJames
źródło
-1

@CandiedOrange podaje ważny punkt w komentarzu do swojej własnej odpowiedzi dotyczącej rozszerzalnych formatów danych, takich jak xml.

Nie powinno to mieć znaczenia, dopóki dodajesz dane. Podaj jednak rozsądne wartości domyślne dla starszych zdarzeń / niepotrzebnych pól.

Musisz tylko zaktualizować usługi, które są dla Ciebie ważne - w tym przypadku - Płeć. Analizator składni xml / json powinien być w stanie zignorować dodatkowe dane dla innych usług. Zależy to oczywiście od wybranego przez ciebie parsera i formatu danych zdarzeń.

Nie zgadzam się jednak z wydarzeniami, które nie mają odpowiednich danych. W przypadku pozyskiwania zdarzeń zdarzenia powinny określać, co się zmieniło. Po otrzymaniu zdarzenia inne usługi nie powinny pobierać danych ze źródła zdarzenia.

Sander Weerdenburg
źródło
Chodzi mi o to, że musi obsługiwać wszelkiego rodzaju zmiany. Pomyśl o usłudze, która transmituje wydarzenie. I to zdarzenie zawiera przestarzałą właściwość, którą należy usunąć. Nawet jeśli te inne usługi nie korzystają z nieruchomości, spowoduje to ich uszkodzenie tylko dlatego, że tego oczekują. Przeczytałem więc ten artykuł na stronie martinfowler.com o umowach konsumenckich: martinfowler.com/articles/consumerDrivenContracts.html Przy stosowaniu tej zasady. Każdy dostawca (wydarzenie) wie, czego się od niego oczekuje. z tą informacją może potwierdzić, jeśli złamie któregoś z graczy.
CGeense,