Od jakiegoś czasu wzorzec “microservices” jest popularny w wielu firmach. Szczegółowe informacje znajdują się na blogu Martin’a Fowler’a, aczkolwiek na blogu chciałbym naszkicować koncepcję.
Pomysł nie jest nowy, jednak pewne usystematyzowanie moim zdaniem ma sens. Dla mnie osobiście, to nic innego jak przestrzeganie zasady “Single rensponsibility” na poziomie usług. Wzorzec ma zastosowanie w systemach złożonych, SOA, a nie w prostych aplikacjach klient-serwer.
Skrajnie zła sytuacja, to taka, w której projekcie występuje jedna wielka usługa – np. napisana w WCF. Powoduje to wiele problemów, a mianowicie:
- Brak jasnej odpowiedzialności. Wyobraźmy sobie, że mamy 20 programistów i tylko jedną, monolitową usługę.
- Release jest bardzo czasochłonny. W celu wdrożenia, należy zebrać programistów z różnych zespołów, ponieważ usługa pokrywa tyle różnych dziedzin.
- “Zero-downtime deployment” jest bardzo utrudniony.
- Ewentualne wycofanie usługi z produkcji (rollback) jest praktycznie niemożliwe ponieważ należałoby przywrócić poprzednią wersję całego systemu.
- Jeśli jakiś komponent przestanie działać, całość systemu nie będzie pracować. W przypadku mikro serwisów, można byłoby wyłączyć jedynie poszczególne węzły.
- Rozproszenie jest trudniejsze i mamy mniejszą kontrolę nad tym.
- Projekty Visual Studio zwykle są duże i trudne w utrzymaniu, aczkolwiek można oczywiście tworzyć mini projekty i je eksponować za pomocą jednej usługi. Zwykle łatwiej jest utrzymać jednak porządek w przypadku kilku niezależnych usług.
- Ciężko wdrożyć kulturę DevOps ze względu na brak jasnej odpowiedzialności.
- Usługa jest wspierana wyłącznie przez kilka osób. Z tego względu, łatwiej utrzymać standardy oraz wizję w jaką stronę kod powinien pójść, niż w przypadku dużych usług modyfikowanych przez wiele zespołów (często z wielu krajów).
Mikro-serwisy, jak sama nazwa sugeruje, polega na tworzeniu wielu usług, które są niezależne od siebie. Zamiast tworzyć jedną usługę, która ma metody typu GetCustomerInfo i GetCustomerAddress, tworzymy kompletnie dwa inne serwisy.
Przez wspomnianą niezależność mam na myśli:
- Osobne repozytorium dla każdego mikro serwisu.
- Osobny projekt\solucja.
- Osobny proces budowania i wdrażania (CI pipelines).
- Możliwość hostowania na osobnych maszynach.
Ostatni punkt wymagana pewnego sprostowania. Bardzo często mikro serwisy hostuje się na tym samym serwerze, ale ze względu, że są to osobne usługi to nic nie stoi na przeszkodzie aby było inaczej.
Pojedyncza usługa powinna być na tyle mała, że jeden zespół (tzn. np. 5 programistów) potrafi utrzymać kilka z nich. Oczywiście istnieją pewne wyzwania i problemy, które nie istnieją w przypadku jednej, wielkiej usługi:
- Cross-cutting concerns – czyli wszelki kod występujący jednocześnie w wielu usługach. Przykładami są autoryzacja, audyt, DAL czy po prostu logi. Nie chcemy w końcu za każdym razem pisać od nowa DAL. Rozwiązaniem jest po prostu wewnętrzne repozytorium NuGet i wszelki kod współdzielony trzymać tam w paczkach.
- Kompatybilność – mamy tak wiele usług i należy zaimplementować pewien mechanizm umożliwiający weryfikację tego. Jak to zrobić, napiszę w przyszłych postach, należy jednak mieć na uwadze, że potencjalnie jest więcej miejsc, gdzie może dojść do konfliktu.
- Zarządzanie wdrożeniami –jak wspomniałem wcześniej, release moim zdaniem są łatwiejsze, ale w tej sytuacji należy mieć inne podejście niż w tradycyjnym, monolitowym wdrażaniu.
- Komunikacja między różnymi procesami jest wolniejsza niż lokalne wywołania metod. Z tego względu, należy zwrócić uwagę na przepływ informacji, aby nie pisać metod, które niewiele robią (“chatty interface”). Jeśli piszemy mikro serwisy od nowa, taki problem zwykle nie występuje. Sytuacja jednak może być kłopotliwa jeśli próbujemy zrefaktoryzować istniejący już system.
Myślę, że warto jeszcze raz podkreślić, że usługi powinny być niezależne od siebie. Jeśli któryś zespół chce korzystać z Dapper zamiast EntiyFramework, nic nie powinno stać na przeszkodzie.
W JustGiving caly proces przechodzenia na mikro-serwisy zaczelismy rok temu. Nie jest to zadanie hop, siup i caly zespol musi byc dobrze zmotywowany do tego przedsiewziecia.
Powoli, powoli wycinamy czesci naszego monoithu i tworzymy z nich micro serwisy. Jak na razie wrazenia sa bardzo pozytywne, co ciekawe dzienny releasy albo szybki hotfix w 10 min robia spore wrazenie na biznesie. Moze to byc dobry argument by przekonac organizacje ze moze tedy droga.
Jesli chodzi o szybkosc dzialania takiego tworu, to odpowiednio skonfigurowany cache powininen zalatwic sprawe do pewnego stopnia. My uzywamy Varnisha, ktory daje ogromne mozliwosci oraz powoduje problemy 🙂 ‘Cache-invalidation’ nie jest problemem trywialnym.
Ogolnie, cale przejscie na micro-serwisy oceniam na ++, mimo tego ze pojawiaja sie pewne zgrzyty i problemy oraz ze trzeba tez duzo zainwestowac w kulture dev-ops i cala infrastrukture.