
Chociaż temat architektury oprogramowania został zamknięty w poprzednim artykule, dzisiejsze zagadnienie bezpośrednio do niego nawiązuje. W tym odcinku serii „Czym jest…?” dowiemy się czym są mikroserwisy – jak je tworzyć, kiedy i w jakim celu.
Uwaga! W tej serii zajmuję się opisowym przedstawieniem tematu, a celem jest zapoznanie z zagadnieniem w sposób luźny i zrozumiały dla nowicjusza. W związku z tym wiele elementów siłą rzeczy musi zostać pominiętych. Tych z was, którzy chcieliby się dowiedzieć więcej, zapraszam do źródeł na końcu artykułu.
2007 był fajnym rokiem. Skończyłem wtedy kolegium nauczycielskie i zdenerwowałem matkę decyzją o nierobieniu licencjatu. Byłem singlem bez zobowiązań, a wolny czas mogłem poświęcać na spotkania z kumplami i łupanie w Lineage 2 razem z członkami klanu, któremu wówczas przewodziłem. Obejrzałem film „Hitman” z Timothy Oliphantem, który może nie zmienił mojego życia, ale okazał się bardzo „pode mnie” i do tej pory mam do niego mały sentyment.

Główny bohater, oparty na postaci z gry pod tym samym tytułem, miał elegancko wszystko rozplanowane. Oglądając Hitmana po raz pierwszy, myślałem że to po prostu kolejny akcyjniak gdzie heros płynie z prądem fabuły przy okazji robiąc rozpierduchę ku uciesze widowni.
To czego wtedy nie zauważyłem to fakt, że produkcja bardziej przypominała partię szachów gdzie wprawny gracz wpierw pozoruje swoją niekompetencję i niby daje się manewrować przez lepszego przeciwnika, tylko po to aby na samym końcu rywal znalazł się dokładnie w tym miejscu gdzie miał się znaleźć od samego początku.
Tylko po co ja o tym piszę?

Cóż, właśnie znalazłeś się dokładnie w tym miejscu gdzie chciałem abyś się znalazł od samego początku.
Nie wiem czy zauważyłeś, ale przez ostatnie siedem odcinków naszego cyklu ANI RAZU nie pokazywałem jak pisać kod produkcyjny. W artykule o Gicie było o wersjonowaniu projektu, w SQL – o bazach danych, nawet w JUnit – choć zbliżyliśmy się do pisania klas i tworzenia obiektów, to wciąż tylko w celach testowych.
Sytuacja się wkrótce zmieni – jak tylko wejdziemy w świat Springa, ale moją intencją było abyśmy do tego czasu mieli opanowane solidne podstawy tworzenia oprogramowania (nie mylić z „programowaniem” w dosłownym znaczeniu tego słowa).
Jeśli jesteś początkującym programistą, a to jest twoja pierwsza wizyta na tej stronie, to gorąco zachęcam do uprzedniego zapoznania się z wcześniejszymi artykułami:
- Czym jest Maven?
- Czym jest Git?
- Czym jest SQL?
- Czym jest JUnit?
- Czym jest REST?
- Czym jest CQRS?
- Czym jest DDD?
Gdybyś teraz nie znał pojęcia „DDD”, to obecny temat mógłby się okazać niezrozumiały. Gdybyś chciał poznać Domain-Driven Design nie wiedząc nic o czystej architekturze to byłoby to daremne. A gdybym ja wcześniej nie wyjaśnił czym jest REST to musiałbym to zrobić teraz, aby zobrazować współpracę pomiędzy poszczególnymi serwisami.
Ale, na szczęście, mamy to już wszystko obcykane, dlatego możemy śmiało ruszyć do przodu!

Monolit
Zacznijmy od czegoś prostego, czegoś co znamy i kochamy, czyli od „monolitu”.
Za tą niemalże magiczną nazwą kryje się zwyczajna aplikacja. Taka napisana np. w Javie, komunikująca się z frontem, z bazą danych, może nawet z jakimś pośrednikiem (ang. message broker, np. RabbitMQ).
Ważne aby cała logika była schowana wewnątrz pakietów należących właśnie do tego programu.
Monolitem jest zatem jakaś prosta gra, np. Tetris czy inne wyścigi. Logika klocków/samochodów znajduje się w modelu, obsługa klawiatury w kontrolerze, a front w widoku. Na bazie danych trzymamy wyniki poszczególnych graczy.
Monolitem może być program typu notatnik, albo lista zadań. Tak długo jak każdy plik/zadanie będzie obsługiwane wewnątrz jednej i tej samej aplikacji, będzie to monolit. Natomiast, jeśli ustawimy np. osobny serwis, który będzie nam wysyłał notatki na maila – w tym miejscu zaczyna się już przygoda z mikroserwisami.

Jakie są zalety monolitu? Dlaczego jest taki popularny?
- przede wszystkim jest łatwy w konstrukcji, nasze pierwsze „Hello world!” jest przecież monolitem
- działa szybko, poszczególne komponenty znajdują się tuż obok siebie przez co czas dostępu liczy się w milisekundach
- bezpieczeństwo: łatwiej zarządzać dostępem do pojedynczego serwisu niż do wielu
Jak się domyślacie powyższe plusy zwartej architektury będą jednocześnie wadami mikroserwisów. I słusznie – trudno mi sobie wyobrazić naukę podstaw programowania od napisania „Witaj świecie!” używając do tego architektury rozproszonej.
Ale mimo powyższych, rozbijanie monolitu na pojedyncze, komunikujące się ze sobą kawałki jest modą, która coraz bardziej nabiera impetu, a wraz z nią mnożą się oferty pracy które jej wymagają. Dlaczego?
Jakie są zalety mikroserwisów?
- skalowalność
W sytuacji kiedy pojedyncza usługa nie wytrzymuje naporu korzystających z niej użytkowników, możemy uruchomić kolejną jej instancję bez potrzeby tracenia zasobów na pozostałe serwisy, które wcale nie potrzebują dodatkowej przepustowości. - odporność na awarie
Gdy padnie jedna usługa monolitu to często oznacza, że pada cała aplikacja. Tutaj z kolei nie działa tylko jedna z wielu funkcjonalności, cała reszta pracuje jakby nic się nie stało. Może nikt nawet tego nie zauważyć. - technologiczna różnorodność
Nic nie stoi na przeszkodzie, aby każdy z niezależnych serwisów był napisany z użyciem innej technologii: języka oprogramowania, lub frameworka, bardziej dopasowanych do przeznaczenia usługi. To sprawia również, że łatwiej zebrać zespół do rozwoju pojedynczego serwisu.

Jak tworzyć mikroserwisy?
Przede wszystkim: to nie jest najlepiej sformułowane pytanie. Powinno ono brzmieć np. „Jak rozbijać monolit na mikroserwisy?”. Zazwyczaj bowiem nie zaczyna się całkiem nowego projektu od rozdzielenia go na mniejsze aplikacje – z przyczyn które wymieniłem jako zalety zwartej architektury: prostota, szybkość, bezpieczeństwo.
Ponadto na samym początku często jeszcze nie wiemy jak aplikacja będzie wyglądać w stanie produkcyjnym – a to jest właśnie podstawa w/w podziału na mniejsze składowe. Najczęściej rozbijamy monolit bo:
- obecny projekt rozrósł się do takich rozmiarów, że samo jego budowanie zajmuje kilka(naście) minut
- zmienił się zespół i np. odszedł jedyny gość, który był w danym projekcie od samego początku, a tylko on ogarniał co gdzie jest
- chcemy go pokroić na tzw. „domeny tematyczne” i rozdzielić za nie odpowiedzialność pomiędzy kilka ekip
Tutaj wspomnę projektowanie zorientowane domenowo (DDD) o którym traktował poprzedni odcinek. Mam nadzieję, że teraz już rozumiecie dlaczego tak ważna jest czysta architektura. Rozdzielenie domen już na etapie tworzenia monolitu bardzo ułatwia późniejszą ich zamianę (wszystkich albo wybranych) na osobne mikroserwisy.

Mikroserwisy: Struktura projektu
Absolutnie nic nie stoi na przeszkodzie, żeby po prostu wycelować jeden mikroserwis tak aby wbił się centralnie w API drugiego serwisu, na tej samej zasadzie jak my możemy to robić za pośrednictwem Postmana czy nawet zwykłej przeglądarki. Powiem więcej – te „strzały” są w mikroserwisach na porządku dziennym i nawet możemy dociągnąć biblioteki (np. Feign Client), które to upraszczają do granic możliwości. Już samo takie posunięcie świadczy, że mamy do czynienia z architekturą rozporoszoną.
Ale to nie wszystko.
Czegoś nam tutaj brakuje do osiągnięcia ZEN i satysfakcjonującego stanu równowagi. Przede wszystkim fajnie by było jakby poszczególne serwisy wiedziały o swoim istnieniu i zawężały swój „scope” (ang. zakres) tylko do innych członków swojego kółka wzajemnej adoracji, zamiast dawać każdemu dostęp na prawo i lewo jak pierwszy lepszy *****szon.
Tylko jak to zrobić? Hmmm…
Eureka!
Otóż to. Eureka Server/Client to kolejny elementy z modułu Spring Cloud, który pomoże nam w stworzeniu siatki serwisów. Ich zadaniem jest to co napisałem powyżej – Eureka Server to jeden z mikroserwisów, bardzo okrojony w funkcjonalności bo jedynym jego zadaniem jest grupowanie pozostałych serwisów (Eureka Client) tak aby te wiedziały o sobie i że mogą sobie wzajemnie zaufać.

Idźmy dalej i przez moment wyobraźmy sobie, że pracujemy we frontendzie. Robimy wygląd strony sklepu: profil użytkownika, oferty, wyszukiwanie, obsługę klienta, koszyk. Każdy z tych elementów odwołuje się do jakiegoś serwisu w backendzie.
Ale do którego?
Zamiast kazać biednemu frontendowcowi spędzać wolny wieczór na pochłanianiu dokumentacji, zróbmy mu dobrze a potem i zaimplementujmy tzw. Gateway’a czyli bramkę proxy. Teraz klient zawsze będzie uderzał wpierw na Zuula (przykładowy gateway) i dopiero tam request zostanie właściwie przekierowany. Gość się nawet nie skapnie, że tam dalej są jakieś mikroserwisy, bo dla niego będzie to wyglądać jak kontakt ze zwykłym monolitem.

Już kończymy, został do omówienia tylko towarzysz Ribbon, który jak na prawdziwego komunistę przystało, rozdziela każdemu po równo.
Jak mówiłem wcześniej – dużą zaletą architektury mikroserwisów jest jej elastyczność i skalowalność. Powiedzmy, że sklep z powyższego przykładu uzbroiliśmy w czat i elementy social media jako osobny serwis. Pomysł chwycił i tysiące użytkowników dyskutuje tam każdego dnia. Nie ma ch..ekhem..bata, żeby jedna instancja obsłużyła wszystkich, więc uruchamiamy parę kolejnych na innych serwerach.
Ribbon, to tzw. ’load balancer’ który jest zespolony z bramką, a jego zadaniem jest takie przekierowanie ruchu aby żadna instancja nie została zablokowana natłokiem zapytań. I również, aby żadna nie leżała bezczynnie.
Podsumowanie
Jak sami widzicie, stworzenie działajacej architektury rozproszonej nie jest jakoś szczególnie wymagającym przedsięwzięciem. Eureka grupuje, Zull w parze z Ribbonem jako proxy, Feign klient do komunikacji REST-owej i gotowe.
Co ciekawe, praktyczne tworzenie takich połączonych projektów może nie różnić się zbytnio od tworzenia monolitu. W IntelliJ, w opcjach Maven’a mamy ikonkę plusika, która pozwala szybko dodać kolejne projekty do tego samego okna dzięki czemu wygląda to jakbyśmy po prostu mieli różne moduły w tej samej appce.

Tym wpisem chciałbym zakończyć część, nazwijmy to „około-programową” gdzie skupialiśmy się na tym jak to wszystko jest ze sobą połączone. W kolejnych odcinkach zajmiemy się wreszcie bezpośrednio pisaniem kodu – z wykorzystaniem Springa, Hibernate’a i message brokerów.
PS. Wiedza, która posłużyła mi do napisania dzisiejszego artykułu pochodzi w głównej mierze z kursu „Mikroserwisy z Spring Cloud” autorstwa Artura Laskowskiego, senior-developera Java udzielającego się również na blogu javasenior.pl.
Chociaż kurs traktuje głównie o architekturze rozproszonej, to jest również świetnym wprowadzeniem do świata Springa i RabbitMQ. Polecam!
Źródła:
Kurs tworzenia mikroserwisów na Udemy (płatne) POL