Architektura mikroserwisów
Podział systemu na mikroserwisy jest to styl projektowania w którym dzielimy funkcjonalność na niezależnie deployowalne serwisy. Każdy z tych serwisów odpowiada za część funkcjonalności systemu, są one często usytuowane na różnych maszynach a komunikacja między odbywa się najczęściej przez HTTP/REST API lub też przez wiadomości przesyłane pomiędzy nimi. Nasuwa się pytanie po co? Po to by ułatwić sobie życie, wyeliminować nieprzespane noce związane z wielkimi i ryzykownymi wdrożeniami oraz by móc powiedzieć, że stosujesz mikroserwisy. Z doświadczenia wiem, że każdy z tych argumentów jest niesamowicie ważny i sprawia, że nie będziesz chcieć wracać do „starego” podejścia.
Wśród najczęściej wymienianych firm wykorzystujących z sukcesami mikroserwisy wymienia się takich potentatów jak: Uber, Netflix czy też PayPal.
Netfix
Uber
PayPal
Architektura oparta o mikroserwisy powinna charakteryzować się tym, że każdy z jej elementów jest luźno powiązany (loosely coupled) z innymi. Tak tak, powinna, lecz „sprawny” programista może ukryć zależności przed niewprawionym okiem i dalej tkwić w monolicie. Tutaj też potrzebny jest stały wysiłek by utrzymywać luźne powiązania, to nie silver bullet (niespodzianka!). Dzięki temu zmiany mogą być wprowadzane niezależnie, przez różne zespoły w różnych częściach systemu.
Wymóg tego by każdy mikroserwis mógł być niezależnie deployowalny jak i to, że ma być luźno powiązany z innymi powoduje, że chcemy też unikać ukrytych zależności takich jak współdzielenie baz danych pomiędzy mikroserwisami. Każdy mikroserwis powinien mieć swoją bazę danych. Taki wymóg oczywiście niesie za sobą konsekwencje i musimy zapewnić spójność danych pomiędzy wieloma serwisami.

Co zyskujemy?
Jakie są zalety takiej architektury? Na początku nie byłem świadomy ich istnienia, zaraz po ich poznaniu lista zalet to były po prostu obietnice. Teraz, po latach spędzonych na pracy nad mikroserwisami widzę, że nie były to puste obietnice.
1. Każdy serwis może być osobno rozwijany.
Wybór technologii dla jednego serwisu często (ale nie zawsze) nie narzuca technologii dla współpracujących serwisów. Mikroserwisy komunikujące się przez HTTP/REST są tu szczególnie łatwe w izolacji. Przykładowo w projekcie nad którym obecnie pracuję wykorzystujemy mikroserwisy w językach c#, python czy też javascript. Umożliwiamy dzięki temu eksplorację innych technologii poprzez wykorzystanie jej np. w jednym z mikroserwisów. Dzięki tego typu architekturze takie eksperymenty, nawet w wypadku nie do końca pozytywnych rezultatów, nie rzutują negatywnie na cały system.
2. Drastyczne skrócenie interwału pomiędzy releasami.
Dzięki możliwości osobnego wdrażania pojedynczych mikroserwisów, taki release jest zarówno mniej ryzykowny jak i częstszy. Jeden deploy? Dzień jak codzień. 3 deploye? Czemu nie? 5+ deploymentów? Bywa i tak 🙂
3. Zmniejszenie złożoności.
System, poprzez zmniejszenie powiązań między poszczególnymi jego częściami charakteryzuje się mniejszą złożonością co wpływa m. in. na błędogenność i czas jego życia. Architektura monolitu ma tendencje do niekorzystnego spadku produktywności rozwoju na dalszych etapach trwania projektu. Mikroserwisy „zrobione dobrze” są bardziej odporne na próbę czasu przede wszystkim z racji lepszej enkapsulacji logiki, możliwości łatwiejszej izolacji problemów, a także wymuszenia lepszej modularności systemu.
4. Skalowalność.
Umożliwienie skalowania poprzez zwiększanie liczby instancji danego mikroserwisu. Jeżeli zidentyfikujemy mikroserwis, który jest wąskim gardłem w naszym systemie możemy zwielokrotnić liczbę jego instancji poprawiając tym samym wydajność całego systemu. Nie jest to coś co otrzymujemy „za darmo”. Wymaga to odpowiedniej kultury devopsowej. Ba, w moim obecnym projekcie po dłuższym czasie nadal nie wygląda to tak jak powinno. Na szczęście coraz popularniejsze wykorzystanie usług chmurowych upraszcza wprowadzenie skalowalności znacząco! Przykładowo AWS EC2 czy też AWS ECS .
5. Bardziej naturalny podział pracy.
Lepsza alokacja zasobów, łatwiejszy podział prac i zwiększona odpowiedzialność zespołów za serwisy, którymi się opiekują to następne z zalet tego podejścia do projektowania systemu. Architektura monolityczna w której odpowiedzialność jest bardziej rozmyta nie sprzyja efektywnej pracy.
6. Circut breaker.
Jeżeli coś nie działa to jest duża szansa, że reszta systemu jest w stanie działać mimo to. Jak uzyskać tego typu niezawodność? Możemy wykorzystać tu podejście w którym definiujemy pewnego rodzaju komponent, który przy odpytywaniu zależności, którą jest inny mikroserwis, a właśniwie jego API, kontroluje stan zdrowia tego API. Jeżeli stan zdrowia przez kilka kolejnych zapytań nie jest zadowalający, przykładowo brak jest połączenia z serwisem, bądź zwraca on HTTP 500 możemy zdecydować o wyłączeniu funkcjonalności jaką oferuje. Dzięki temu nie odpytujemy na darmo serwisu który być może tylko tymczasowo jest niedysponowany oraz dajemy użytkownikowi szansę skorzystania z innych funkcjonalności systemu.
Oczywiście lista zalet nie wyczerpuje w pełni tematu, gdyż architekturę mikroserwisów można zrealizować na różne sposoby przez co możemy uzyskać nieco inny ich zestaw. Osobiście wydaje mi się, że zmniejszona złożoność systemu to największy plus gdyż jest to istotny czynnik który wpływa na opłacalność projektów, a nadzwyczaj często jest niedoceniany.
Bibliografia i inne ciekawe materiały:
Pingback: Mikroserwisy – smutne(?) fakty i hejting | Me, Software and stuff