Mikroserwisy w akcji. Отсутствует
Kolejka zadań dystrybuuje je od 1 do n odbiorców
Nasza brama giełdy może działać w następujący sposób. Każde zlecenie utworzone przez usługę obsługi zleceń wywoła zdarzenie OrderCreated, które zostanie umieszczone w kolejce tak, aby zostało przekazane dalej przez usługę bramy giełdy. Ten wzorzec jest przydatny, gdy:
■ istnieje relacja 1: 1 między zdarzeniem a zadaniem do wykonania w odpowiedzi na to zdarzenie;
■ zadanie, które należy wykonać, jest złożone lub czasochłonne, dlatego należy je wykonywać poza zdarzeniem wyzwalającym.
Domyślnie to podejście nie wymaga zaawansowanego dostarczania zdarzeń. Dostępnych jest wiele bibliotek kolejek zadań, które korzystają z magazynów danych, takich jak Redis (Resque, Seler, Sidekiq) lub baz danych SQL.
PUBLISH-SUBSCRIBE
We wzorcu publish-subscribe usługi generują zdarzenia dla dowolnych odbiorców. Wszyscy odbiorcy, którzy odbiorą zdarzenie, otrzymują je na wyłączne posiadanie. Pod pewnymi względami jest to idealny wzorzec dla mikroserwisów – usługa może wysyłać dowolne zdarzenia do świata bez dbania o to, kto później będzie je przetwarzał (rys. 3.13).
Rysunek 3.13. Jak publish-subscribe wysyła zdarzenia do subskrybentów
Wyobraźmy sobie na przykład, że po złożeniu zlecenia chcemy śledzić działania wykonywane na niższym szczeblu. Możemy chcieć wysłać do klienta powiadomienie push, dostarczyć statystyki dotyczące zlecenia lub jakieś inne rekomendacje. Wszystkie te funkcjonalności mogą nasłuchiwać tego samego zdarzenia.
3.4.4. Lokalizowanie innych usług
Aby podsumować tę część, poświęćmy chwilę na zbadanie wykrywania usług. Aby usługi mogły się komunikować, muszą być w stanie się nawzajem wykrywać. Warstwa platformy powinna oferować taką możliwość.
Podstawowym podejściem do wykrywania usług jest stosowanie mechanizmów równoważenia obciążenia (rys. 3.14). Na przykład elastyczny moduł równoważenia obciążenia (elastic load balancer, ELB) w systemie AWS ma przypisaną nazwę DNS i zarządza sprawdzaniem działania węzłów podstawowych, opierając się na przynależności do grupy maszyn wirtualnych (grupa automatycznego skalowania w systemie AWS).
Rysunek 3.14. Wykrywanie usług za pomocą mechanizmów równoważenia obciążenia i nazw z DNS
To działa, ale nie obsługuje bardziej złożonych scenariuszy. A co, jeśli chcemy przekierować ruch do innej wersji kodu, aby włączyć wdrożenia kanarkowe (canary deployments) lub dark launch, lub jeśli chcemy przekierować ruch między różnymi centrami danych?
Bardziej wyrafinowanym podejściem jest skorzystanie z rejestru, takiego jak Consul (https://www.consul.io). Instancje usług zgłaszają się do rejestru zapewniającego interfejs API – za pośrednictwem DNS lub specjalnego mechanizmu rozpoznawania żądań dla tych usług. Na rysunku 3.15 pokazano to podejście.
Rysunek 3.15. Wykrywanie usług za pomocą rejestru usług jako źródła prawdy
Potrzeby związane z wykrywaniem usług będą zależeć od złożoności topologii wdrożonej aplikacji. Bardziej złożone wdrożenia, takie jak rozproszone geograficznie, wymagają bardziej niezawodnej architektury wykrywania usług10.
UWAGA Po wdrożeniu do Kubernetesa w rozdziale 9 dowiemy się o usługach, mechanizmie, którego Kubernetes używa do umożliwiania wykrywania.
3.5. Granica aplikacji
Warstwa granicy stanowi fasadę w stosunku do złożonych interakcji wewnętrznych usług. Klienci, na przykład aplikacje mobilne, webowe interfejsy użytkownika lub urządzenia IoT, mogą wchodzić w interakcję z aplikacją mikroserwisową. (Można budować tych klientów samodzielnie lub mogą je tworzyć inne podmioty korzystające z publicznego interfejsu API naszej aplikacji). Na przykład SimpleBank, jak pokazano na rysunku 3.16, ma wewnętrzne narzędzia administracyjne, witrynę inwestycyjną, aplikacje na iOS i Androida oraz publiczny interfejs API.
Rysunek 3.16. Aplikacje klienckie w SimpleBank
Warstwa graniczna zapewnia abstrakcyjność nad wewnętrzną złożonością i zmiennością (rys. 3.17). Można na przykład zapewnić spójny interfejs dla klienta, który będzie wyświetlał listę wszystkich zleceń historycznych, ale z czasem można całkowicie zreorganizować wewnętrzną implementację tej funkcjonalności. Bez tej warstwy klienci wymagaliby zbyt dużej wiedzy na temat poszczególnych usług i byliby ściśle związani z implementacją systemu.
Rysunek 3.17. Granica stanowi fasadę nad warstwą usług, aby ukryć przed konsumentem wewnętrzną złożoność
Poza tym, warstwa graniczna zapewnia dostęp do danych i funkcjonalności przy użyciu komunikacji i rodzaju treści odpowiedniej dla konsumenta. Na przykład, podczas gdy usługi mogą komunikować się między sobą za pomocą gRPC, fasada może udostępniać HTTP API zewnętrznym konsumentom, co jest znacznie bardziej odpowiednie dla nich do zastosowania.
Połączenie tych ról pozwala aplikacji stać się czarną skrzynką, wykonującą dowolne operacje (nieznane klientowi) w celu zaoferowania funkcjonalności. Z większą pewnością można także wprowadzać zmiany w warstwie usług, ponieważ klient łączy się z nią za pośrednictwem pojedynczego punktu.
Warstwa granicy może również implementować inne zdolności przeznaczone dla klientów:
■ uwierzytelnianie i autoryzacja – aby zweryfikować tożsamość i uprawnienia klienta API;
■ ograniczanie liczby odpowiedzi – aby zapewnić ochronę przed nadużyciami klientów;
■ buforowanie – aby zmniejszyć ogólne obciążenie backendu;
■ zbieranie dzienników i metryk – aby umożliwić analizę i monitorowanie żądań klientów.
Umieszczenie tych krańcowych zdolności w warstwie granicznej zapewnia ich wyraźny rozdział – bez granicy usługi backendowe musiałyby indywidualnie implementować te zdolności, zwiększając tym samym swoją złożoność.
Granic można także użyć na poziomie usług, aby oddzielić ich domeny. Na przykład proces składania zleceń może się składać z kilku usług, ale tylko jedna z nich powinna udostępniać punkt dostępu, do którego byłby dostęp z innych domen (rys. 3.18).
Rysunek 3.18. Granice mogą występować między różnymi kontekstami w aplikacji mikroserwisowej
UWAGA Wewnętrzne granice usługi często odzwierciedlają ograniczone konteksty – spójne, ograniczone podzbiory ogólnego zakresu aplikacji. Omówimy je dokładniej w następnym rozdziale.
To tylko przegląd możliwości zastosowania granic. Bądźmy bardziej konkretni i przyjrzyjmy się bliżej trzem różnym (choć powiązanym) wzorcom dla granic aplikacji: bramom API, bramom consumer-driven oraz backends for frontends.
3.5.1. Bramy API
Wzorzec