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ń?
Odpowiedzi:
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ę.
źródło
UserRegistered
zdarzenie, 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.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.
źródło
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ł.
źródło
@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.
źródło