Mikrousługi: jak obsługiwać relacje klucza obcego

85

Architektura mikrousług sugeruje, że każda usługa powinna obsługiwać własne dane. W związku z tym każda usługa (Usługa A) zależna od danych należących do innej usługi (usługa B) powinna uzyskiwać dostęp do takich danych nie poprzez bezpośrednie wywołania DB, ale za pośrednictwem interfejsu API udostępnianego przez drugą usługę (usługę B).

Co zatem sugerują najlepsze rozwiązania dotyczące mikrousług dotyczących sprawdzania ograniczeń klucza obcego.

Przykład: Opracowuję funkcję dostawy (mikrousługa 1) dla produktów, a niektóre produkty są dostępne tylko w określonych lokalizacjach, jak wspomniano w tabeli produktów, dostępnej tylko dla mikroserwisów produktów (mircoservice 2).

Jak się upewnić, że mikrousługa 1 (tj. Funkcja dostawy) nie przenosi zamówienia do lokalizacji bez obsługi. Mam to pytanie, ponieważ funkcja dostawy nie ma bezpośredniego dostępu do bazy danych produktów, więc nie ma ograniczeń obowiązujących na poziomie DB, gdy zamówienie dostawy jest składane w bazie danych dostaw (nie jest możliwe sprawdzenie, czy w bazie danych produktów istnieje dopasowanie klucza obcego lub stół).


źródło

Odpowiedzi:

71

Możliwe jest użycie udostępnionej bazy danych dla wielu mikrousług. Wzorce zarządzania danymi mikrousług można znaleźć pod tym linkiem: http://microservices.io/patterns/data/database-per-service.html . Nawiasem mówiąc, jest to bardzo przydatny blog dotyczący architektury mikrousług.

W Twoim przypadku wolisz używać bazy danych na wzorzec usługi. Dzięki temu mikrousługi są bardziej autonomiczne. W takiej sytuacji należy zduplikować niektóre dane w wielu mikrousługach. Możesz udostępniać dane za pomocą wywołań interfejsu API między mikrousługami lub możesz udostępniać je za pomocą komunikatów asynchronicznych. To zależy od Twojej infrastruktury i częstotliwości zmian danych. Jeśli nie zmienia się często, należy zduplikować dane ze zdarzeniami asynchronicznymi.

W Twoim przykładzie usługa dostawy może powielać lokalizacje dostawy i informacje o produkcie. Serwis produktowy zarządza produktami i lokalizacjami. Następnie wymagane dane są kopiowane do bazy danych usługi Delivery z komunikatami asynchronicznymi (można na przykład użyć rabbit mq lub apache kafka). Usługa dostawy nie zmienia danych produktu i lokalizacji, ale wykorzystuje te dane podczas wykonywania swojej pracy. Jeśli część danych produktu, która jest używana przez usługę dostawy, często się zmienia, powielanie danych za pomocą wiadomości asynchronicznych będzie bardzo kosztowne. W takim przypadku należy wykonywać wywołania interfejsu API między usługą Product i Delivery. Usługa dostawy prosi obsługę produktu o sprawdzenie, czy produkt można dostarczyć do określonej lokalizacji, czy nie. Usługa dostawy prosi obsługę produktów o identyfikator (nazwa, identyfikator itp.) Produktu i lokalizacji. Te identyfikatory mogą być pobierane od użytkownika końcowego lub są współużytkowane przez mikrousługi. Ponieważ bazy danych mikrousług są tutaj różne, nie możemy definiować kluczy obcych między danymi tych mikrousług.

Wywołania API mogą być łatwiejsze do wdrożenia, ale w tej opcji koszt sieci jest wyższy. Twoje usługi są również mniej autonomiczne, gdy wykonujesz wywołania API. Ponieważ w twoim przykładzie, gdy usługa produktu nie działa, usługa dostawy nie może wykonać swojej pracy. W przypadku duplikowania danych za pomocą wiadomości asynchronicznych dane wymagane do dostarczenia znajdują się w bazie danych mikrousługi dostarczania. Gdy usługa produktu nie działa, będziesz mógł dokonać dostawy.

Ali Sağlam
źródło
2
Świetna odpowiedź. Używam wywołań API, ale wymaga to również sortowania i paginacji danych z innej usługi. Czy znasz najlepsze podejście do tej sprawy?
tranceholic
6
Należy dodać parametry związane ze stronicowaniem i sortowaniem do swojego interfejsu API. Wówczas odpowiedzialność za uzyskanie odpowiedniej strony z odpowiednią kolejnością przejmą konsumenci API. Istnieją technologie używane do definiowania interfejsu API, takie jak GraphQL. O ile wiem, te technologie mają już funkcje sortowania i paginacji. Jeśli nie korzystasz z tego rodzaju technologii, możesz po prostu pobrać parametry od klienta i użyć ich do zwrócenia danych posortowanych według stron.
Ali Sağlam
1
Naprawdę świetna odpowiedź!
TS
2
Ale czy zachowujesz klucz obcy? Np .: każdy post na blogu będzie zawierał wiele komentarzy. Monolith będzie miał tabelę komentarzy z kluczem obcym do wpisu na blogu. Jednak w mikrousługach będziemy mieli dwie usługi. Usługa 1: Post Microservie z tymi polami tabeli (PostID, Name, Content) Usługa 2: Comments Microservie z tymi polami tabeli (CommentID, PostID, Cpmment) Pytanie brzmi, czy potrzebujemy "PostID" w usłudze 2 (Comments Microservice)? Wydaje mi się, że odpowiedź brzmi tak, ponieważ musimy wiedzieć, który komentarz należy do którego postu. Czy moje rozumienie jest prawidłowe?
rakesh mehra
1
Jak podzielić system na mikrousługi to zupełnie inna historia, ale jeśli zdecydowałeś się utworzyć 2 mikrousługi, takie jak post i komentarz, potrzebujesz identyfikatora posta w mikrousłudze komentarzy, ponieważ każdy komentarz należy do postu. Nie oznacza to jednak, że musisz zdefiniować FK między tymi tabelami. FK jest tylko ograniczeniem w świecie RDBMS, które pomaga zapewnić integralność i spójność danych. Jeśli przechowujesz dane tych mikrousług w oddzielnych schematach, nie możesz zdefiniować FK, a nawet możesz zachować swoje dane w bazie danych nosql (co miałoby sens w przypadku mikrousług komentarzy), gdzie FK nie ma zastosowania.
Ali Sağlam
24

Podczas dystrybucji kodu w celu uzyskania zmniejszonego sprzężenia chcesz uniknąć udostępniania zasobów, a dane są zasobami, których chcesz uniknąć.

Inną kwestią jest to, że tylko jeden komponent w twoim systemie jest właścicielem danych (dla operacji zmiany stanu), inne komponenty mogą CZYTAĆ, ale NIE PISAĆ, mogą mieć kopie danych lub możesz udostępniać model widoku, którego mogą użyć, aby uzyskać najnowszy stan przedmiotu.

Wprowadzenie integralności referencyjnej przywróci sprzężenie, zamiast tego chcesz użyć czegoś takiego jak Guidy dla kluczy podstawowych, zostaną one utworzone przez twórcę obiektu, a reszta dotyczy zarządzania ostateczną spójnością.

Zapoznaj się z przemówieniem Udi Dahana w NDC Oslo, aby uzyskać więcej informacji

Mam nadzieję że to pomoże

Sean Farmar
źródło
2
Link do przemówienia Udi Dahana jest bardzo interesujący
Comencau
3

pierwsze rozwiązanie: skład API

 Implement a query by defining an API Composer, which invoking the
 services that own the data and performs an in-memory join of the
 results

wprowadź opis obrazu tutaj

drugie rozwiązanie: CQRS

Define a view database, which is a read-only replica that is designed to support that 
query. The application keeps the replica up to data by subscribing to Domain events 
published by the service that own the data.

wprowadź opis obrazu tutaj

Ali_Hr
źródło
1

Aktualizacja tej odpowiedzi w 2020 roku polega na użyciu narzędzia do przechwytywania danych zmian, takiego jak Debezium. Debezium będzie monitorować tabele bazy danych pod kątem zmian i przesyłać je strumieniowo do Kafka / Pulsar (inne potoki), a Twoi subskrybenci mogą następnie przechwytywać zmiany i synchronizować je.

user521990
źródło