Mikroserwisy i podejście do nich powinno być wykonywane z zastosowaniem wzorców projektowych.
W tym wpisie przybliżymy Wam wzorzec
Service Discovery.
Wzorzec ten wykorzystywany jest do rejestracji i udostępniania różnych instancji mikroserwisów w celu realizacji zadań, o które proszą klienci usługi.
Jak to się dzieje ? Czy robimy to sami?
Nie, w tym celu stosuje się rejestr (registry), który dokładnie wie jakie mikroserwisy oraz ile ich instancji jest do naszej dyspozycji.
Najpierw mikroserwisy dodają się do rejestru, a następnie ten rejestr udostępnia je zainteresowanym klientom.
Tak więc klienci nie odwołują się do tych mikroserwisów bezpośrednio, tylko korzystają z pośrednika w postaci rejestru.
To ten pośrednik wybiera, która instancja mikroserwisu wykona daną pracę.
Innymi słowy „odkrywa, który serwis wykona pracę” – czyli wykona "service discovery".
Nasze pierwsze Service Registry
W ramach zadania przygotujemy sobie następujące elementy:
- Projekt eureka.server
To właśnie będzie nasz rejestr mikroserwisów, a zarazem pośrednik w komunikacji z nimi.
- Projekt eureka.microservice
Implementacja mikroserwisu, który w momencie uruchamiania będzie automatycznie dodawał się do rejestru.
Projekt eureka.server
Wchodzimy na stronę
Spring initializera i tworzymy
projekt jak na poniższym zdjęciu. Poza metadanymi projektu i wersją Spring Boot-a niezwykle ważne jest dodanie zależności (dependency)
spring-cloud-netflix Eureka Server. Po skonfigurowaniu pobieramy projekt
(Generate) i
rozpakowujemy go na dysku.
Dalej importujemy projekt jako
Maven project do naszego IDE. Efekt importu jest taki jak poniżej.
W celu zwiększenia czytelności zmieniliśmy nazwę wygenerowanej klasy z
Application na
EurekaServer.
Teraz musimy jeszcze
włączyć funkcję rejestracji i udostępniania mikroserwisów (service discovery), realizowaną przez nasz projekt dzięki zależności
spring-cloud-netflix Eureka Server
(adnotacja
@EnableEurekaServer pochodzi z pakietu
org.springframework.cloud.netflix.eureka.server):
Kolejny krokiem jest dostarczenie konfiguracji polegającej na zdefiniowaniu portu, pod którym będzie dostępna nasza "usługa odkrywcza".
Konfigurację dodajemy do pliku
application.properties (
/src/main/resources/application.properties):
Uruchomienie Eureka Service Registry
Uruchamiamy naszą usługę, uważnie obserwujemy konsolę i odnotowujemy, że na konsoli pojawił się...wyjątek:
Co ciekawe tak skonfigurowana usługa uruchomi się i będziemy mogli jej używać.
Nie będzie to jednak w pełni satysfakcjonujące, dopóki nie rozwiążemy zaistniałego problemu.
Wyjątek pojawia się, ponieważ nie istnieje żadna replikacja (replica node), do której nasza usługa mogłaby się podpiąć.
Domyślnie eureka serwer oczekuje nodów do replikacji, ponieważ w środowisku produkcyjnym wymagana jest elastyczność i
niezawodność. W takim środowisku usługa rejestru powinna być wielokrotnie replikowana.
Natomiast my nie potrzebujemy teraz omawiać replikacji, ponieważ chcemy jedynie uruchomić prosty rejestr usług
w postaci jednej instancji, do której będzie mogło podpiąć się wiele mikroserwisów (a także wiele instancji każdego z nich).
Tak więc co robimy w tej sytuacji? Okazuje się, że możemy wyłączyć "dopytywanie się" eureki o nody replikacyjne.
W tym celu dodajemy do
application.properties własność
eureka.client.fetch-registry,
którą ustawiamy na
false.
W celu zwiększenia czytelności warto też wyłączyć opcję rejestracji serwera eureki w samym sobie -
eureka.client.register-with-eureka.
Po wprowadzeniu tych zmian i ponownym uruchomieniu usługi, na konsoli nie pojawi się już żaden wyjątek.
Oczekujemy na pełne uruchomienie rejestru, co zostanie nam oznajmione wpisem podobnym do poniższego:
Weryfikujemy, czy mamy dostęp do usługi poprzez wejście na stronę pod
adresem url, który zdefiniowaliśmy wcześniej
w pliku konfiguracyjnym
application.properties:
Na uwagę z pewnością zasługuje fakt, że w sekcji
Instances currently registered with Eureka
nie mamy na tym etapie jeszcze żadnego zarejestrowanego mikroserwisu. Co ciekawe, gdybyśmy pozwolili na rejestrację serwisu Eureki (obecnie omawianego projektu), usuwając wpis
eureka.client.register-with-eureka z
application.properties, wówczas instancja tego serwisu pojawiłaby się
tutaj jako zarejestrowana "sama w sobie":
Projekt eureka.microservice
Teraz przyszedł czas na stworzenie pierwszego mikroserwisu, który zarejestruje się a naszym rejestrze.
Ponownie wchodzimy na stronę
Spring initializera i tworzymy
projekt, tyle że teraz nazywamy go inaczej -
eureka.microservice.
Poza metadanymi projektu i wersją Spring Boot-a niezwykle ważne jest dodanie zależności (dependency)
spring-cloud-netflix Eureka Client (uwaga - poprzednio dodawaliśmy
Eureka Server).
Po skonfigurowaniu pobieramy projekt
(Generate) i rozpakowujemy go na dysku.
Dalej importujemy projekt jako
Maven project do naszego IDE. Efekt importu jest taki jak poniżej.
W celu zwiększenia czytelności zmieniliśmy nazwę wygenerowanej klasy z
Application na
EurekaMicroservice.
Teraz musimy jeszcze
przekształcić nasz projekt do postaci klienta Eureki, co jest realizowane dzięki zależności
spring-cloud-netflix Eureka Discovery Client
(adnotacja
@EnableEurekaClient pochodzi z pakietu
org.springframework.cloud.netflix.eureka):
Kolejny krokiem jest dostarczenie konfiguracji polegającej na zdefiniowaniu portu, pod którym będzie dostępny nasz mikroserwis.
Konfigurację dodajemy do pliku
application.properties (
/src/main/resources/application.properties).
Appa Notka.
Pamiętaj, że wprowadzamy usługę Eureki między innymi po to, by móc łatwo dodawać w przyszłości kolejne instancje danego mikroserwisu.
W takiej sytuacji trudno byłoby zdefiniować porty na sztywno dla każdej instancji z osobna. Pozwalamy więc, aby numery portów były dobierane losowo.
Nie ustawiamy portu na sztywno. Zamiast tego podajemy wartość
0, która oznacza, że dla każdej kolejnej instancji porty będą dobierane losowo.
W pliku dodajemy również nazwę naszej usługi (
spring.application.name). To ona będzie widoczna na konsoli Eureki w kolumnie
Application.
Oprócz tego definiujemy własność
eureka.instance.instance-id, która będzie unikalną nazwą generowaną dla każdej instancji.
Wygenerowanie unikalnego identyfikatora jest zapewnione przez
random:value:
Zanim uruchomimy w IntelliJ kilka instancji tego samego projektu mikroserwisu, należy umożliwić uruchamianie równoległe.
Otwieramy okno
Run/Debug Configurations i tam zaznaczamy checkbox
Allow parallel run:
Uruchomienie i rejestracja kilku instancji mikroserwisu
Uruchamiamy kilka mikroserwisów. Właściwie wystarczą nawet już dwa, aby zaobserwować, co się dzieje.
Pierwsza instancja startuje z losowo wybranym portem i rejestruje się na konsoli za pomocą randomowego identyfikatora:
Startujemy drugą instancję i ponownie widzimy nowy port oraz to, że kolejna instancja dodała się do rejestru Eureki:
Appa Notka.
Dotarłe(a)ś do tego miejsca, realizując kolejne punkty w tym rozdziale?
Gratulacje!
Właśnie stworzyłe(a)ś swój pierwszy rejestr mikroserwisów na podstawie wzorca Service Discovery!
W kolejnym rozdziale (dostępnym wkrótce) podłączymy się klientem do rejestru Eureki, która udostępni klientowi wybraną instancję mikroserwisu,
w celu wykonania określonego zadania.
Self-preservation vs wyłączanie mikroserwisu
Na początku nauki Eureki w tym miejscu zwykle zaczyna się problem.
Okazuje się, że wyłączając instancję mikroserwisu, ciągle widzimy ją dostępną na konsoli Eureki (tak jakby nadal działała).
Spowodowane jest to tym, że domyślnie Eureka ma włączony tryb
self-preservation, czyli
chronienia usług przed usunięciem z rejestru, nawet jeśli nie ma z nimi kontaktu.
Usługa wysyła regularnie tzw.
heartbeat do Eureki, dzięki czemu wiadomo, że dana usługa ciągle "żyje".
Czasem jednak zdarza się, że takie sygnał nie przychodzi, co może oznaczać, iż usługa została niespodziewanie wyłączona.
Natomiast Eureka "woli" jednak założyć, że mogą to być jedynie przejściowe trudności w komunikacji, na przykład kłopot z połączeniem sieciowym
i z tego powodu nie wyrejestrowuje takiej usługi (dodatkowym kryterium jest liczba wyłączonych instancji danego mikroserwisu przekraczająca 85% wszystkich instancji).
O ile w środowisku produkcyjnym istotnym kryterium jest niezawodność w dłuższej perspektywie, o tyle
w prostym przykładzie edukacyjnym tworzonym na własnej maszynie takie ustawienie może przeszkadzać.
Można temu zaradzić ustawiając odpowiednią własność w pliku konfiguracyjnym
application.properties (
preservation) na wartość
false.
Przy okazji warto również zmienić czas uruchamiania joba sprawdzającego, które instancje są niedostępne
i usuwającego (
evict) te instancje z rejestru. Tak więc finalnie plik konfiguracyjny dla
eureka.server będzie wyglądał tak:
Appa Notka.
Uwaga. W momencie wyłączenia trybu ochrony konsola Eureki będzie wyświetlała komunikat jak na poniższym zdjęciu.
Pamiętając o tym, że pracujemy lokalnie i chcemy jedynie edukować się w temacie Eureki, nie musimy się tym przejmować.
Oczywiście w środowisku produkcyjnym (a nawet i środowiskach pre-produkcyjnych należy opcję ochrony pozostawić włączoną).
W mikroserwisie rejestrującym się w Eurece także wprowadzimy pewne modyfikacje. Skrócimy
czas wygaśnięcia dzierżawy oraz
jej
odświeżenia.
Teraz do momentu wyłączenia mikroserwisu po około 5 sekundach nastąpi wyrejestrowanie go z Eureki (zniknie z listy
Instances currently registered with Eureka).
Autor: Kasia Kowolik
Data: 06 października 2020
Labele:Backend, Poziom średniozaawansowany, Java, Mikroserwisy, Spring, Spring Boot
Linki:
Spring Boot Initializr
https://github.com/javappa-coding/eureka-service-discovery