Mikrousługi bez powielania danych

20

Trudno mi uniknąć duplikacji danych lub wspólnej bazy danych dla nawet najprostszego projektu mikrousług, co sprawia, że ​​myślę, że coś mi brakuje. Oto podstawowy przykład problemu, przed którym stoję. Zakładając, że ktoś używa aplikacji internetowej do zarządzania zapasami, potrzebowałby dwóch usług; jeden dla zapasów zarządzających towarami i ilością w magazynie oraz usługą użytkowników, która zarządzałaby danymi użytkowników. Jeśli chcemy przeprowadzić kontrolę tego, kto zaopatrzył bazę danych, moglibyśmy dodać identyfikator użytkownika do bazy danych dla usługi inwentaryzacji jako ostatni magazynowany według wartości.

Korzystając z aplikacji, możemy chcieć zobaczyć wszystkie wyczerpujące się produkty oraz listę osób, które je ostatnio zaopatrzyły, abyśmy mogli poprosić ich o ponowne uruchomienie. Korzystając z opisanej powyżej architektury, do usługi inwentaryzacyjnej zostanie wysłane żądanie pobrania szczegółów pozycji dla wszystkich towarów, których ilość jest mniejsza niż 5. Zwróci to listę zawierającą identyfikatory użytkowników. Następnie do serwisu użytkowników zostanie wysłane oddzielne żądanie uzyskania nazwy użytkownika i danych kontaktowych dla listy identyfikatorów użytkowników uzyskanych z usługi inwentaryzacji.

Wydaje się to okropnie nieefektywne i nie wymaga wielu innych usług, zanim wykonamy wiele żądań do interfejsów API różnych usług, które z kolei wykonują wiele zapytań do bazy danych. Alternatywą jest replikacja danych użytkowników w danych inwentaryzacyjnych. Gdy użytkownik zmieni swoje dane kontaktowe, będziemy musieli powtórzyć zmianę za pośrednictwem wszystkich innych usług. Ale to wydaje się nie pasować do ograniczonej kontekstowej koncepcji mikrousług. Możemy również użyć jednej bazy danych i udostępnić ją między różnymi usługami oraz mieć wszystkie problemy związane z integracją bazy danych .

Jaki jest właściwy / najlepszy sposób na wdrożenie tego?

Geraint Anderson
źródło
5
Witamy w paradoksie mikrousług. To, co wydaje się upraszczać, może w rzeczywistości bardziej skomplikować sprawę.
Robert Harvey
„Właściwy” sposób jest taki sam, jak zawsze: znajdź sposób robienia rzeczy, które najlepiej odpowiadają twoim konkretnym celom.
Robert Harvey
1
@RobertHarvey Tak jest zawsze, ale staram się zrozumieć sposób mikrousług w podręczniku. Kiedy zrozumiem, jak powinien działać w idealnym świecie, chętnie go zmienię, aby pasował do mojego przypadku użycia.
Geraint Anderson
1
Ale musisz sformułować swoje pytanie pod względem wydajności, co jest niefunkcjonalnym wymaganiem oprogramowania. Sposób rozwiązania problemu z wydajnością polega na bezpośrednim zapytaniu do bazy danych.
Robert Harvey
1
Właśnie miałem napisać pytanie dokładnie tak, jak twoje. Nadal nie widzę zalet MSA dla stosunkowo prostych aplikacji internetowych. Myślę, że w wielu przypadkach modułowość mogłaby zostać osiągnięta bez komplikowania spraw.
Glasnhost

Odpowiedzi:

10

Zupełnie tęskniłem za tym, gdzie trzeba skopiować.

Główną zasadą mikrousług jest to, aby usługa była pojedynczym organem. Oznacza to, że zarządzanie zapasami i użytkownikami może być całkowicie oddzielne. Zaprojektowałbym zarządzanie użytkownikami tak, aby nawet nie wiedziało, że istnieje system zapasów.

Ale zaprojektowałbym system ekwipunku, aby nigdy nie przechowywał niczego o użytkownikach innych niż ID użytkownika. To rozwiązuje problem propagowania zmian informacji o użytkowniku.

Jeśli chodzi o rzeczy, które wymagają zarówno informacji o inwentarzu, jak i informacji o użytkowniku, takich jak dzienniki, audyty i wydruki, nie są aktualizowane po zmianie informacji. Są zapisem tego, co było. Znowu nie propagujesz zmian.

Tak więc w każdym przypadku, gdy chcesz uzyskać najnowsze informacje o użytkowniku, pytasz serwis informacyjny użytkownika.

candied_orange
źródło
@Geraint: Czy możesz sprecyzować, jakiego rodzaju powielanie występuje w twoim systemie?
Robert Harvey
1
Dzięki. Powielanie dotyczyło kopiowania danych kontaktowych użytkowników do usługi inwentaryzacji, ale rozwiązałeś to (tj. Nie jest to wymagane). Przejście z pojedynczej relacyjnej bazy danych, w której mogłem uzyskać dane inwentaryzacyjne i dane użytkownika, przyłączyłem do wykonania dwóch różnych wywołań API, w których drugie nie może się rozpocząć, dopóki pierwsza nie zwróci wyników. Ale to chyba część oceny, czy używam mikrousług, czy czegoś innego.
Geraint Anderson
To ta sama sztuczka, której użyłaby DB, gdyby zarządzała obydwoma. Nie kopiujesz informacji o użytkowniku do tabeli ekwipunku. Dajesz mu klucz obcy. Identyfikator użytkownika wykonuje tę samą pracę w różnych usługach. Po prostu spraw, aby był wyjątkowy.
candied_orange
It seems counter-intuitive to move from a single relational database where I could get the inventory data and the user data with a joinPamiętaj, że „idealnie” jest jeden sklep na usługę (lub więcej!). Nie ma więc czegoś takiego jak „łączenie” między „granicami”. Powód jest prosty: DB generuje sprzężenie między usługami. W przeciwieństwie do sugestii @CandiedOrange, myślę, że możemy zduplikować minimum danych z jednej usługi do drugiej. Mam na myśli dane, których zmiana jest mało prawdopodobna. Jeśli ten duplikat poprawi wydajność i wydajność (i oba są wymagane), „plusy” prawdopodobnie zrównoważyłyby „minusy”
Laiv
@GeraintAnderson Mam na myśli, że jeśli potrzebujesz wydajności (która z definicji jest wymogiem niefunkcjonalnym), są na to sposoby. Tj. Żądaj stron danych z usługi magazynowej (np. 10 elementów), weź każdą stronę i użyj tej strony, aby poprosić o dane z usługi użytkownika, i na koniec agreguj. W ten sposób utrzymujesz swoje granice, wykorzystując równoległość niezależnych usług. Nawet wtedy nie przejmuj się, dopóki nie zidentyfikujesz go jako prawdziwego wąskiego gardła aplikacji, którą należy rozwiązać - czekanie dodatkowych 1/2 sekundy na 1-sekundową nocną pracę nie ma znaczenia dla nikogo.
Delioth,
11

Trudno mi uniknąć powielania danych ...

Według ebooka Microsoft na temat architektury mikrousług , duplikacja danych nie jest niczym złym. Zasadniczo duplikacja danych zwiększa rozdzielenie usług i tym samym wzmacnia ich rolę jako jednego organu. Odpowiedni fragment:

I wreszcie (i właśnie tam powstaje większość problemów podczas budowania mikrousług), jeśli twoja początkowa mikrousługa potrzebuje danych, które pierwotnie były własnością innych mikrousług, nie polegaj na synchronicznym żądaniu tych danych. Zamiast tego replikuj lub propaguj te dane (tylko potrzebne atrybuty) do bazy danych usługi początkowej przy użyciu ostatecznej spójności (zwykle przy użyciu zdarzeń integracji ...

Maurits Moeys
źródło
1
Całkowicie się nie zgadzam. Utrudnia to utrzymanie. Umożliwia wdrażanie transakcji między mikrousługami, gdy trzeba coś dodać, zaktualizować lub usunąć. Jeśli chcesz zapobiec pojedynczemu punktowi awarii, możesz użyć żądania lub innego rodzaju buforowania.
Alan Sereb,
1
@AlanSereb Trudniej jest utrzymać, ale czasami chodzi o to, że nie masz innego wyjścia. Na przykład co zrobić, jeśli trzeba utworzyć FK między obiektami mieszkającymi w dwóch bazach danych? Jedynym sposobem zapewnienia spójności podczas wykonywania zapytań w lokalnej bazie danych jest replikacja danych. Spójrz na: stackoverflow.com/a/4452586/2255491
David D.
Zgadzam się. Innym doskonałym podejściem jest wybranie trasy pozyskiwania wydarzeń. I niech wszystkie mutacje zostaną wykonane za pośrednictwem potoku zdarzeń
Alan Sereb
4

do usługi inwentaryzacyjnej zostanie wysłane żądanie pobrania szczegółów pozycji dla wszystkich towarów, których ilość jest mniejsza niż 5. Zwróci to listę zawierającą identyfikatory użytkowników. Następnie do serwisu użytkowników zostanie wysłane oddzielne żądanie uzyskania nazwy użytkownika i danych kontaktowych dla listy identyfikatorów użytkowników uzyskanych z usługi inwentaryzacji.

W rzeczy samej tak.

To prawda, że ​​w monolicie można mieć model zapasów, w którym można wyszukiwać odpowiednie elementy, wprowadzać je do modelu użytkownika i uzyskiwać te same dane.

Lub możesz pójść dalej, jeśli masz je w tej samej relacyjnej bazie danych i napisz SQL, a baza danych zajmie tabelę inwentarza i tabelę użytkownika, to trochę magii i otrzymasz dane, których szukasz.

Niezależnie od tego, jak to robisz, gdzieś tam będzie kod, który zasadniczo pobiera listę identyfikatorów użytkowników z systemu ekwipunku, podaje je do systemu użytkownika i kompiluje listę danych.

Pytanie, na które musisz odpowiedzieć, dotyczy wydajności i konserwacji oraz innych „miękkich” cech.

Główną zaletą mikrousług jest skalowanie. Jeśli masz dziesięć tysięcy użytkowników na jednym komputerze i jest to trochę powolne, możesz dodać inną maszynę, a system staje się dwa razy szybszy. Dodaj jeszcze osiem, a to dziesięć razy szybciej. (Skalowanie liniowe jest prawdopodobnie optymistyczne, ale jest idealne i nie jest to nierozsądne nadziei).

I to jest za usługę . Jeśli system inwentarza stanowi wąskie gardło, jest używany do więcej niż raportów o użytkownikach, możesz dodać więcej maszyn tylko do tej usługi . Maszyny mogą być również wyspecjalizowane; ta usługa wymaga dużo pamięci, ta usługa wykonuje ciężkie obliczenia i potrzebuje więcej procesora.

Jeśli skalowanie nie jest potrzebne, mikrousługi mają jeszcze jedną zaletę: są one modułowe . Oczywiście, monolityczne aplikacje mogą być również modułowe, a ty masz znormalizowaną bazę danych i ... ale w praktyce ściany między modułami są w najlepszym przypadku jak szklane ściany, aw najgorszym - linie na piasku. Mikrousługi są oddzielone litą stalą.

Jeśli system użytkownika dosłownie się zapali, nie wpłynie to w żaden sposób na system ekwipunku. Nie będziesz w stanie wydrukować ładnych raportów o tym, kto co zaopatrzył, ale klienci będą mogli bezpiecznie składać zamówienia, wiedząc, że są tam zapasy.

A ty nie zduplikowane dane w microservices , nie bardziej niż ty w relacyjnej bazie danych (*). W relacyjnej bazie danych możesz wykonać sprzężenie , a odpowiednikiem jest scalenie list w kodzie, jak opisano.

Możesz także dodać widok , odpowiednikiem jest dodanie nowej usługi, która dokonuje scalenia za Ciebie; spowodowałoby to trzy wnioski; jeden do nowej usługi, a następnie ta usługa wykonuje oryginalne dwa. Relacyjne bazy danych mają fantazyjne elementy, które optymalizują widoki, które muszą zostać zaimplementowane na poziomie usługi. Nie dostaniesz go „za darmo”.

Buforowanie różni się od powielania danych tym, że jeśli dwie wartości są niezgodne, wiesz, która z nich jest niepoprawna. Jest często stosowany w mikrousługach, aby zwiększyć dostępność kosztem spójności (twierdzenie CAP). Ponieważ relacyjne bazy danych całkowicie masują dostępność na ołtarzu spójności, jest to w nich mniej powszechne. Powiedziałbym, że nic nie jest związane z mikrousługami, które ułatwiają buforowanie, ale w praktyce buforowanie jest podstawową kwestią i sprawia, że buforowanie jest łatwiejsze w mikrousługach .

(*) Jeśli ma sens powielanie danych w roju mikrousług, prawdopodobnie miałoby to sens w równoważnej relacyjnej bazie danych.

Odalrick
źródło
3
Naprawdę podobała mi się twoja odpowiedź, dopóki nie „nie powielaj danych w mikrousługach”. Myślę, że istnieją przypadki, w których duplikacja danych jest właściwym podejściem. Poprawia to odporność na awarie i autonomię. Jeśli usługa użytkownika uległa awarii, usługa magazynowa może nadal wyświetlać listę niskich zapasów wraz z tym, kto je ostatnio zaopatrzył.
Peter Pompeii
1
@peterpompeii Nazwałbym to buforowaniem, a nie duplikacją danych. Duplikacja danych ma miejsce, gdy masz dwa miejsca na aktualizację dla jednego układu odniesienia, buforowanie, gdy jest jedno miejsce i automatyczne propagowanie do innych miejsc. Powiedziałem też więcej niż relacyjny. Jeśli w relacyjnej bazie danych ma sens powielanie danych, ma to sens w mikrousługach. Myślę, że się zgadzamy i ta część może być jaśniejsza, ale teraz mam tylko telefon, więc nie zaktualizuję teraz tekstu.
Odalrick
@PeterPompeii Mam nadzieję, że dodana sekcja o buforowaniu rozwiązuje niektóre z twoich problemów.
Odalrick
1
@Odalrick to, co opisałeś, przypomina replikację danych. Replikacji i buforowania są obie formy powielania danych. Replikacja ma miejsce, gdy gwarantuje się, że kopia zawsze zawiera wszystkie potrzebne dane. Buforowanie jest dostępne na żądanie. Buforowanie może mieć wadę. Buforowanie dostępności nie ma tak dużego sensu, jak buforowanie wydajności. TL; DR, jeśli przechowujesz pełną kopię czegoś z wystarczającą spójnością, gwarantuje, że nigdy nie musisz sprawdzać brakujących danych, nie jest to pamięć podręczna.
Brandon,
1
@Brandon Kolejną różnicą między replikacją a buforowaniem jest to, skąd wiadomo, które dane są nieprawidłowe, gdy występuje różnica. Replikacja określa niektóre reguły dotyczące scalania danych. Z drugiej strony buforowanie jest zawsze : pamięć podręczna jest zła.
Odalrick