Architektura mikrousług obejmuje kolekcję małych, autonomicznych usług. Każda usługa jest samodzielna i powinna implementować pojedynczą funkcję biznesową w ograniczonym kontekście. Ograniczony kontekst jest naturalnym podziałem w firmie i zapewnia jawną granicę, w której istnieje model domeny.
Co to są mikrousługi?
Mikrousługi są małe, niezależne i luźno powiązane. Tego typu usługi może pisać i obsługiwać jeden niewielki zespół deweloperów.
Każda usługa stanowi oddzielną bazę kodu, która może być zarządzana przez mały zespół deweloperów.
Usługi można wdrażać niezależnie. Zespół może zaktualizować istniejącą usługę bez ponownego kompilowania i wdrażania całej aplikacji.
Usługi są odpowiedzialne za utrwalanie własnych danych lub stanu zewnętrznego. Odróżnia je to od tradycyjnego modelu, w którym trwałość danych jest obsługiwana przez odrębną warstwę danych.
Usługi komunikują się ze sobą za pomocą dobrze zdefiniowanych interfejsów API. Szczegóły wewnętrznej implementacji poszczególnych usług są ukryte przed innymi usługami.
Obsługuje programowanie wielolotowe. Na przykład usługi nie muszą współdzielić tego samego stosu technologii, bibliotek ani struktur.
Oprócz samych usług w typowej architekturze mikrousług występują również inne składniki:
Zarządzanie/aranżacja. Ten składnik umożliwia między innymi umieszczanie i ponowne równoważenie usług w węzłach oraz identyfikowanie awarii. Zazwyczaj jest to gotowa technologia, taka jak usługa Kubernetes, raczej niż niestandardowe rozwiązanie.
Brama interfejsu API. Brama interfejsu API jest punktem wejścia dla klientów. Zamiast bezpośredniego wywołania usług klienci wywołują bramę interfejsu API, która przekazuje wywołanie do odpowiednich usług na zapleczu.
Zalety korzystania z bramy interfejsu API są następujące:
Klienci są oddzieleni od usług. Usługi można wersjonować lub refaktoryzować bez konieczności aktualizowania wszystkich klientów.
Usługi mogą używać protokołów obsługi komunikatów, które nie współdziałają z Internetem, na przykład protokół AMQP.
Brama interfejsu API może wykonywać inne ogólne funkcje, takie jak uwierzytelnianie, rejestrowanie, kończenie żądań SSL i równoważenia obciążenia.
Gotowe zasady, takie jak ograniczanie przepustowości, buforowanie, przekształcanie lub walidacja.
Świadczenia
Zwinność Mikrousługi są wdrażane niezależnie, dlatego łatwiej zarządzać poprawkami błędów i wersjami funkcji. Usługę można zaktualizować bez konieczności ponownego wdrożenia całej aplikacji i wycofać aktualizację, jeśli coś się nie uda. W wielu tradycyjnych aplikacjach wystąpienie usterki w jednej części aplikacji może zablokować cały proces wydania. Nowe funkcje mogą być wstrzymane w oczekiwaniu na zintegrowanie, przetestowanie i opublikowanie poprawki błędów.
Małe, ukierunkowane zespoły. Mikrousługa powinna być na tyle mała, aby mógł ją utworzyć, przetestować i wdrożyć jeden zespół funkcji. Małe rozmiary zespołów przyczyniają się do zwiększenia elastyczności. Duże grupy bywają mniej produktywne, ponieważ komunikacja jest wolniejsza, zwiększa się obciążenie związane z zarządzaniem, a zmniejsza się elastyczność.
Niewielka baza kodu. W aplikacji monolitycznej występuje tendencja z upływem czasu, aby zależności kodu stały się splątane. Dodanie nowej funkcji wymaga dotykania kodu w wielu miejscach. Architektura mikrousług, która nie korzysta z udostępnionego kodu ani udostępnionych magazynów danych, ogranicza takie zależności, co ułatwia dodawanie nowych funkcji.
Mieszanina technologii. Zespoły mogą wybierać technologie, które najlepiej pasują do ich usług, korzystając z różnych stosów technologii w zależności od potrzeb.
Izolacja błędów. Jeśli pojedyncza mikrousługa stanie się niedostępna, nie spowoduje to zakłócenia całej aplikacji, o ile wszystkie nadrzędne mikrousługi są zaprojektowane pod kątem prawidłowego obsługi błędów. Można na przykład zaimplementować wzorzec wyłącznika lub zaprojektować rozwiązanie tak, aby mikrousługi komunikowały się ze sobą przy użyciu wzorców asynchronicznych obsługi komunikatów.
Skalowalność. Usługi można skalować niezależnie — możesz skalować te podsystemy, które wymagają większej ilości zasobów, bez konieczności skalowania całej aplikacji. Za pomocą orkiestratora, takiego jak Kubernetes, można spakować większą gęstość usług na jednym hoście, co pozwala na bardziej wydajne wykorzystanie zasobów.
Izolacja danych. Znacznie łatwiej przeprowadza się aktualizacje schematów, ponieważ dotyczy to tylko pojedynczych mikrousług. W aplikacji monolitycznej aktualizacje schematu mogą stać się bardzo trudne, ponieważ różne części aplikacji mogą dotykać tych samych danych, co ryzykowne zmiany schematu.
Wyzwania
Zalety mikrousług mają jednak swoją cenę. Poniżej przedstawiono niektóre trudności, które należy wziąć pod uwagę przed rozpoczęciem tworzenia architektury mikrousług.
Złożoność. Aplikacja korzystająca z mikrousług ma więcej „ruchomych części” niż równoważna aplikacja monolityczna. Poszczególne usługi są prostsze, ale cały system jest bardziej złożony.
Tworzenie i testowanie. Pisanie małej usługi, która opiera się na innych usługach zależnych, wymaga innego podejścia niż pisanie tradycyjnej aplikacji monolitycznej lub warstwowej. Nie wszystkie dostępne narzędzia współdziałają z zależnościami usług. Refaktoryzacja wykraczająca poza granice usług może być trudna. Utrudnione jest również testowanie zależności usług, zwłaszcza jeśli aplikacja szybko się zmienia.
Brak nadzoru. Zdecentralizowana metoda tworzenia mikrousług ma zalety, ale może również powodować problemy. W pewnym momencie używane języki i struktury mogą być tak różnorodne, że utrzymywanie aplikacji stanie się trudne. Przydatne może okazać się wprowadzenie standardów dla całego projektu bez zbytniego ograniczania elastyczności zespołów. Dotyczy to zwłaszcza kompleksowych funkcji, takich jak rejestrowanie.
Przeciążenie i opóźnienie sieci. Korzystanie z wielu małych, precyzyjnych usług może być przyczyną nasilenia komunikacji między usługami. Ponadto jeśli łańcuch zależności usług znacznie się wydłuży (usługa A wywołuje usługę B, który wywołuje usługę C itd.), problemem może stać się dodatkowe opóźnienie. Należy uważnie projektować interfejsy API. Unikaj nadmiernie czatty interfejsów API, pomyśl o formatach serializacji i poszukaj miejsc do użycia asynchronicznych wzorców komunikacji, takich jak bilansowanie obciążenia oparte na kolejce.
Integralność danych. Każda mikrousługa odpowiedzialna za trwałość danych. W rezultacie spójność danych w wielu usługach może stanowić wyzwanie. Różne usługi utrwalają dane w różnych momentach, używając różnych technologii i potencjalnie różnych poziomów sukcesu. Jeśli więcej niż jedna mikrousługi jest zaangażowanych w utrwalanie nowej lub zmienionej daty, jest mało prawdopodobne, aby kompletna zmiana danych mogła zostać uznana za transakcję ACID. Zamiast tego technika jest bardziej wyrównana do base (w zasadzie dostępne, stan miękki i ostatecznie spójne). W miarę możliwości należy uwzględniać spójność ostateczną.
Zarządzanie Skuteczne korzystanie z mikrousług wymaga dojrzałej metodyki DevOps. Problemem może okazać się skorelowanie rejestrowania między usługami. Zazwyczaj w ramach rejestrowania należy skorelować wiele wywołań usługi dla pojedynczej operacji użytkownika.
Przechowywanie wersji. Aktualizacje usługi nie mogą przerwać działania usług zależnych. W dowolnym momencie można zaktualizować szereg usług, dlatego niezachowanie ostrożności podczas projektowania może spowodować występowanie problemów ze zgodnością z poprzednimi lub następnymi wersjami.
Zestaw umiejętności. Mikrousługi to systemy bardzo rozproszone. Należy wnikliwie zastanowić się, czy członkowie zespołu mają odpowiednie umiejętności i doświadczenie, wymagane do pracy z mikrousługami.
Najlepsze rozwiązania
Model usług buduj wokół domeny biznesowej.
Wszystko powinno być zdecentralizowane. Poszczególne zespoły mają zajmować się projektowaniem i tworzeniem usług. Unikaj udostępniania kodu lub schematów danych.
Usługa, która jest właścicielem danych, powinna używać prywatnego magazynu danych. Dopasuj magazyn pod kątem poszczególnych usług i typów danych.
Usługi komunikują się za pośrednictwem dobrze zaprojektowanych interfejsów API. Unikaj ujawniania szczegółów implementacji. Interfejsy API powinny modelować domenę, a nie wewnętrzną implementację usługi.
Unikaj tworzenia sprzężeń między usługami. Przyczyny sprzężeń obejmują współdzielone schematy bazy danych i sztywne protokoły komunikacyjne.
Zmniejsz obciążenie kompleksowymi funkcjami, takimi jak uwierzytelnianie i kończenie żądań SSL, przenosząc je do bramy.
Odizoluj informacje o domenie od bramy. Obsługa żądań klientów i ich kierowanie przez bramę powinny odbywać się bez korzystania z żadnych informacji dotyczących reguł biznesowych lub logiki domeny. W przeciwnym razie nastąpi uzależnienie bramy, co może spowodować sprzężenie między usługami.
Usługi powinny mieć luźne powiązania i wysoki poziom spójności funkcjonalnej. Funkcje, które prawdopodobnie będą zmieniane grupowo, powinny zostać umieszczone w pakiecie i być wdrażane razem. Jeśli zostaną one umieszczone w oddzielnych usługach, usługi te staną się ściśle powiązane, ponieważ zmiana jednej z nich będzie wymagać aktualizacji innej usługi. Zbytnie nasilenie komunikacji między dwoma usługami może być objawem ścisłego sprzężenia i niskiej spójności.
Odizoluj błędy. Stosowanie strategii odpornościowych pozwala zapobiec występowaniu błędów kaskadowych w usłudze. Zobacz Wzorce odporności i Projektowanie niezawodnych aplikacji.
Następne kroki
Aby uzyskać szczegółowe wskazówki dotyczące tworzenia architektury mikrousług na platformie Azure, zobacz Projektowanie, tworzenie i obsługa mikrousług na platformie Azure.