JavaRush /Blog Java /Random-PL /Przewodnik po mikrousługach Java. Część 1: Podstawy i arc...

Przewodnik po mikrousługach Java. Część 1: Podstawy i architektura mikrousług

Opublikowano w grupie Random-PL
W tym przewodniku dowiesz się, czym są mikroserwisy Java, jak je projektować i tworzyć. Obejmuje także pytania dotyczące bibliotek mikrousług Java i możliwości korzystania z mikrousług. Tłumaczenie i adaptacja mikrousług Java: praktyczny przewodnik .

Mikrousługi Java: podstawy

Aby zrozumieć mikrousługi, musisz najpierw zdefiniować, czym one nie są. Czy nie jest to „monolit” – monolit Java: czym jest i jakie są jego zalety i wady? Przewodnik po mikrousługach Java.  Część 1: Podstawy i architektura mikrousług - 1

Co to jest monolit Java?

Wyobraź sobie, że pracujesz dla banku lub startupu fintech. Udostępniasz użytkownikom aplikację mobilną, za pomocą której mogą otworzyć nowe konto bankowe. W kodzie Java spowoduje to obecność klasy kontrolera. W uproszczeniu wygląda to tak:
@Controller
class BankController {

    @PostMapping("/users/register")
    public void register(RegistrationForm form) {
        validate(form);
        riskCheck(form);
        openBankAccount(form);
        // etc..
    }
}
Potrzebujesz kontrolera do:
  1. Potwierdzono formularz rejestracyjny.
  2. Sprawdziłem ryzyko związane z adresem użytkownika, aby zdecydować, czy udostępnić mu konto bankowe.
  3. Otworzył konto bankowe.
Klasa BankControllerzostanie spakowana wraz z resztą źródeł w pliku bank.jar lub bank.war w celu wdrożenia — jest to stary, dobry monolit zawierający cały kod potrzebny do uruchomienia banku. Z grubsza szacuje się, że początkowy rozmiar pliku .jar (lub .war) będzie wynosić od 1 do 100 MB. Teraz możesz po prostu uruchomić plik .jar na swoim serwerze... i to wszystko, co musisz zrobić, aby wdrożyć aplikację Java. Przewodnik po mikrousługach Java.  Część 1: Podstawy i architektura mikrousług - 2Zdjęcie, lewy górny prostokąt: wdrożenie mono(litycznego) banku java -jar bank.jar (cp .war/.ear do serwera aplikacji). Prawy prostokąt: otwórz przeglądarkę.

Jaki jest problem z monolitami Java?

Nie ma nic złego w monolitach Java. Jednak doświadczenie pokazało, że jeśli w Twoim projekcie:
  • Pracuje wielu programistów/zespołów/konsultantów...
  • ...nad tym samym monolitem pod presją klientów o bardzo niejasnych wymaganiach...
  • w ciągu kilku lat...
... w tym przypadku mały plik bank.jar zamienia się w sam ogromny gigabajt kodu, do którego nawet podejście jest przerażające, a co dopiero wdrożenie.

Jak zmniejszyć rozmiar monolitu Java?

Powstaje naturalne pytanie: jak zmniejszyć monolit? W tej chwili Twój plik bank.jar działa na jednej maszynie JVM, jeden proces na jednym serwerze. Nie więcej nie mniej. I w tej chwili może przyjść nam do głowy logiczna myśl: „Ale z usługi weryfikacji ryzyka mogą korzystać inne działy w mojej firmie! Nie jest to bezpośrednio powiązane z moją monolityczną aplikacją bankową! Może warto go wyciąć z monolitu i wstawić jako osobny produkt? Oznacza to, technicznie rzecz biorąc, uruchomienie go jako oddzielnego procesu Java.

Co to jest mikrousługa Java?

W praktyce to zdanie oznacza, że ​​teraz wywołanie metody riskCheck()nie będzie wykonane z BankControllera: ta metoda lub komponent bean wraz ze wszystkimi jej klasami pomocniczymi zostanie przeniesiony do własnego projektu Maven lub Gradle. Zostanie również wdrożony i objęty kontrolą wersji niezależnie od monolitu bankowego. Jednakże cały ten proces ekstrakcji nie powoduje przekształcenia nowego modułu RiskCheck w mikrousługę samą w sobie, ponieważ definicja mikrousługi jest otwarta na interpretację. Prowadzi to do częstych dyskusji w zespołach i firmach.
  • Czy 5-7 zajęć w projekcie jest mikro czy co?
  • 100 czy 1000 zajęć... nadal mikro?
  • Czy mikrousługi są ogólnie powiązane z liczbą klas, czy nie?
Zostawmy teoretyczne rozważania i zamiast tego trzymajmy się rozważań pragmatycznych i zróbmy tak:
  1. Nazwijmy wszystkie oddzielnie wdrożone usługi mikrousługami, niezależnie od ich rozmiaru i granic domeny.
  2. Zastanówmy się, jak zorganizować komunikację między służbami. Nasze mikrousługi potrzebują sposobów komunikowania się ze sobą.
Podsumowując: wcześniej miałeś jeden proces JVM, solidny monolit do prowadzenia banku. Teraz masz bankowy proces JVM i oddzielną mikrousługę RiskCheck, która działa w ramach własnego procesu JVM. A teraz, aby sprawdzić ryzyko, Twój monolit musi wywołać tę mikrousługę. Jak to zrobić?

Jak nawiązać komunikację między mikroserwisami Java?

Ogólnie rzecz biorąc, istnieją dwie opcje - komunikacja synchroniczna i asynchroniczna.

Komunikacja synchroniczna: (HTTP)/REST

Zazwyczaj zsynchronizowana komunikacja między mikrousługami odbywa się za pośrednictwem usług HTTP i REST, które zwracają XML lub JSON. Oczywiście mogą istnieć inne opcje - weź przynajmniej Bufory protokołu Google . Jeśli potrzebujesz natychmiastowej reakcji, lepiej skorzystać z komunikacji REST. W naszym przykładzie właśnie to należy zrobić, ponieważ przed otwarciem konta wymagana jest weryfikacja ryzyka. Jeśli nie ma kontroli ryzyka, nie ma konta. Narzędzia omówimy poniżej, w sekcji „ Które biblioteki są najlepsze do synchronicznych wywołań Java REST ”.

Przesyłanie wiadomości — komunikacja asynchroniczna

Asynchroniczną komunikację mikrousługową zwykle realizuje się poprzez wymianę komunikatów z implementacją JMS i/lub przy użyciu protokołu, takiego jak AMQP . Nie bez powodu napisaliśmy tutaj „zwykle”: powiedzmy, że nie można niedoceniać liczby integracji poczty e-mail/SMTP. Użyj go, gdy nie potrzebujesz natychmiastowej reakcji. Przykładowo, użytkownik klika przycisk „kup teraz”, a Ty z kolei chcesz wygenerować fakturę. Proces ten z pewnością nie powinien zachodzić w cyklu żądanie zakupu-odpowiedź użytkownika. Poniżej opiszemy, które narzędzia są najlepsze do asynchronicznego przesyłania komunikatów w języku Java .

Przykład: wywołanie API REST w Javie

Załóżmy, że wybieramy synchroniczną komunikację mikrousług. W tym przypadku nasz kod Java (ten, który przedstawiliśmy powyżej) na niskim poziomie będzie wyglądał mniej więcej tak. (przez niski poziom rozumiemy tutaj fakt, że w przypadku komunikacji mikrousług zwykle tworzone są biblioteki klienckie, które odrywają Cię od rzeczywistych wywołań HTTP).
@Controller
class BankController {

    @Autowired
    private HttpClient httpClient;

    @PostMapping("/users/register")
    public void register(RegistrationForm form) {
        validate(form);
        httpClient.send(riskRequest, responseHandler());
        setupAccount(form);
        // etc..
    }
}
Na podstawie kodu staje się jasne, że musimy teraz wdrożyć dwie (mikro) usługi Java, Bank i RiskCheck. W rezultacie będziemy mieć uruchomione dwa procesy JVM. Przewodnik po mikrousługach Java.  Część 1: Podstawy i architektura mikrousług - 3To wszystko, czego potrzebujesz, aby opracować projekt mikrousług Java: po prostu zbuduj i wdróż mniejsze fragmenty (pliki .jar lub .war) zamiast jednej monolitycznej. Odpowiedź na pytanie pozostaje niejasna: jak podzielić monolit na mikroserwisy? Jak małe powinny być te kawałki, jak określić właściwy rozmiar? Sprawdźmy.

Architektura mikrousług Java

W praktyce firmy rozwijają projekty mikroserwisowe na różne sposoby. Podejście zależy od tego, czy próbujesz przekształcić istniejący monolit w projekt mikrousług, czy zaczynasz projekt od zera.

Od monolitu do mikrousług

Jednym z najbardziej logicznych pomysłów jest wyodrębnienie mikrousług z istniejącego monolitu. Należy zauważyć, że przedrostek „mikro” w tym przypadku nie oznacza w rzeczywistości, że wyodrębnione usługi będą naprawdę niewielkie; niekoniecznie tak jest. Spójrzmy na tło teoretyczne.

Pomysł: rozbić monolit na mikrousługi

Podejście oparte na mikrousługach można zastosować w przypadku starszych projektów. I własnie dlatego:
  1. Najczęściej takie projekty są trudne w utrzymaniu/zmianie/rozbudowie.
  2. Wszyscy, od programistów po zarząd, chcą uproszczeń.
  3. Masz (stosunkowo) jasne granice domeny, co oznacza, że ​​dokładnie wiesz, co powinno robić Twoje oprogramowanie.
Wracając do naszego przykładu, oznacza to, że możesz przyjrzeć się monolitowi bankowości Java i spróbować rozbić go na granice domen.
  • Rozsądne byłoby zatem wydzielenie przetwarzania danych użytkowników (takich jak nazwiska, adresy, numery telefonów) do osobnej mikrousługi „Zarządzanie kontem”.
  • Albo wspomniany wcześniej „Moduł Kontroli Ryzyka”, który sprawdza poziom ryzyka użytkownika i może być wykorzystywany przez wiele innych projektów, a nawet działów firmy.
  • Lub moduł fakturowania, który wysyła faktury w formacie PDF lub pocztą.

Realizacja pomysłu: pozwól to zrobić komuś innemu

Opisane powyżej podejście wygląda świetnie na papierze i diagramach przypominających UML. Jednak nie wszystko jest takie proste. Jego praktyczne wdrożenie wymaga poważnego przygotowania technicznego: rozbieżność między naszym rozumieniem tego, co fajnie byłoby wydobyć z monolitu, a samym procesem ekstrakcji jest ogromna. Większość projektów korporacyjnych osiąga etap, w którym programiści boją się, powiedzmy, uaktualnić 7-letnią wersję Hibernate do nowszej. Biblioteki będą aktualizowane wraz z nim, ale istnieje realne niebezpieczeństwo, że coś zepsuje. Zatem ci sami programiści muszą teraz przekopywać się przez starożytny kod z niejasnymi granicami transakcji w bazie danych i wyodrębniać dobrze zdefiniowane mikrousługi? Najczęściej problem ten jest bardzo złożony i nie da się go „rozwiązać” ani na tablicy, ani na spotkaniach poświęconych architekturze. Przewodnik po mikrousługach Java.  Część 1: Podstawy i architektura mikrousług - 4Cytując twórcę Twittera @simonbrown: Będę to powtarzać w kółko… jeśli ludzie nie potrafią poprawnie budować monolitów, mikrousługi nie pomogą. Simona Browna

Projekt od podstaw w oparciu o architekturę mikroserwisową

W przypadku nowych projektów Java trzy ponumerowane elementy z poprzedniej części wyglądają nieco inaczej:
  1. Zaczynasz z czystym kontem, więc nie musisz utrzymywać żadnego „bagażu”.
  2. Twórcy chcą, aby w przyszłości wszystko było proste.
  3. Problem: Masz znacznie bardziej niejasny obraz granic domen: nie wiesz, co tak naprawdę ma robić Twoje oprogramowanie (wskazówka: zwinny ;))
Prowadzi to do tego, że firmy próbują nowych projektów z mikrousługami Java.

Architektura techniczna mikrousług

Pierwszy punkt wydaje się być najbardziej oczywisty dla deweloperów, choć są też tacy, którzy zdecydowanie go odradzają. Hadi Hariri zaleca refaktoryzację „Extract Microservice” w IntelliJ. I choć poniższy przykład jest bardzo uproszczony, to realizacje zaobserwowane w rzeczywistych projektach niestety nie odbiegają od niego zbytnio. Przed mikroserwisami
@Service
class UserService {

    public void register(User user) {
        String email = user.getEmail();
        String username =  email.substring(0, email.indexOf("@"));
        // ...
    }
}
Z podciągiem mikrousługi Java
@Service
class UserService {

    @Autowired
    private HttpClient client;

    public void register(User user) {
        String email = user.getEmail();
        //теперь вызываем substring microservice via http
        String username =  httpClient.send(substringRequest(email), responseHandler());
        // ...
    }
}
Zasadniczo zawijasz wywołanie metody Java w wywołaniu HTTP, bez wyraźnego powodu. Jest jednak jeden powód: brak doświadczenia i próba narzucenia podejścia opartego na mikroserwisach Java. Zalecenie: nie rób tego.

Architektura mikrousług zorientowana na przepływ pracy

Kolejnym powszechnym podejściem jest podzielenie mikrousług Java na moduły oparte na przepływie pracy. Przykład z życia wzięty: w Niemczech, gdy udajesz się do lekarza (publicznego), musi on zarejestrować Twoją wizytę w swoim medycznym systemie CRM. Aby otrzymać zapłatę z ubezpieczenia, prześle on dane dotyczące Twojego leczenia (i leczenia innych pacjentów) do pośrednika poprzez XML. Broker sprawdzi ten plik XML i (w uproszczeniu):
  1. Sprawdzi, czy otrzymano poprawny plik XML.
  2. Sprawdzi wiarygodność zabiegów: powiedzmy, roczne dziecko, które w ciągu jednego dnia otrzymało od ginekologa trzy zabiegi czyszczenia zębów, wygląda nieco podejrzanie.
  3. Połączy XML z innymi biurokratycznymi danymi.
  4. Prześle plik XML do firmy ubezpieczeniowej w celu zainicjowania płatności.
  5. I wyśle ​​wynik do lekarza, przekazując mu komunikat „sukces” lub „proszę o ponowne przesłanie tego nagrania, gdy tylko będzie to miało sens”.
Notatka. W tym przykładzie komunikacja pomiędzy mikroserwisami nie odgrywa roli, ale równie dobrze mogłaby być realizowana asynchronicznie przez brokera komunikatów (np. RabbitMQ), ponieważ lekarz i tak nie otrzymuje natychmiastowej informacji zwrotnej. Przewodnik po mikrousługach Java.  Część 1: Podstawy i architektura mikrousług - 5Ponownie wygląda to świetnie na papierze, ale pojawiają się naturalne pytania:
  • Czy do przetworzenia jednego pliku XML konieczne jest wdrożenie sześciu aplikacji?
  • Czy te mikrousługi są rzeczywiście od siebie niezależne? Czy można je wdrażać niezależnie od siebie? Z różnymi wersjami i schematami API?
  • Co robi mikrousługa wiarygodności, jeśli mikrousługa weryfikacyjna nie działa? Czy system nadal działa?
  • Czy te mikrousługi korzystają z tej samej bazy danych (z pewnością potrzebują wspólnych danych w tabelach DB), czy też każdy z nich ma własną?
  • … i wiele więcej.
Co ciekawe, powyższy diagram wygląda na prostszy, ponieważ każda usługa ma teraz swój własny, ściśle określony cel. Kiedyś wyglądało to mniej więcej tak jak ten straszny monolit: Przewodnik po mikrousługach Java.  Część 1: Podstawy i architektura mikrousług - 6choć można się spierać co do prostoty tych diagramów, teraz zdecydowanie trzeba rozwiązać te dodatkowe wyzwania operacyjne.
  • Nie musisz wdrożyć tylko jednej aplikacji, ale co najmniej sześciu.
  • Może nawet zaistnieć potrzeba wdrożenia wielu baz danych, w zależności od tego, jak daleko chcesz zagłębić się w architekturę mikrousług.
  • Musisz upewnić się, że każdy system jest online i działa poprawnie.
  • Musisz upewnić się, że połączenia między mikrousługami są naprawdę odporne (zobacz Jak zapewnić odporność mikrousług Java?).
  • I wszystko inne, co oznacza ta konfiguracja - od lokalnych ustawień programistycznych po testowanie integracji.
Zatem zalecenie byłoby następujące:
  • Jeśli nie jesteś Netfliksem (prawdopodobnie nie jesteś Netfliksem)...
  • Chyba, że ​​masz super silne umiejętności pracy, gdy otwierasz środowisko programistyczne i powoduje to małpę chaosu, która wyrzuca produkcyjną bazę danych, którą można łatwo przywrócić w 5 sekund.
  • lub czujesz się jak @monzo i chcesz wypróbować 1500 mikrousług tylko dlatego, że możesz.
→ Nie rób tego. A teraz jest mniej hiperbolicznie. Próba modelowania mikrousług poza granicami domen wydaje się całkiem rozsądna. Nie oznacza to jednak, że musisz podzielić jeden przepływ pracy na małe, pojedyncze części (odbieranie XML, sprawdzanie poprawności XML, przesyłanie dalej XML). Dlatego za każdym razem, gdy rozpoczynasz nowy projekt z mikrousługami Java, a granice domeny są nadal bardzo niejasne, staraj się utrzymywać rozmiar mikrousług na niskim poziomie. Zawsze możesz później dodać więcej modułów. I upewnij się, że masz zaawansowany DevOps w zespole/firmie/oddziale, aby wspierać Twoją nową infrastrukturę.

Architektura mikroserwisowa zorientowana na zespół lub poliglota

Istnieje trzecie, niemal libertariańskie podejście do tworzenia mikrousług: umożliwienie zespołom, a nawet pojedynczym osobom, wdrażania historyjek użytkowników przy użyciu dowolnej liczby języków lub mikrousług (marketerzy nazywają to podejście „programowaniem poliglotycznym”). Zatem opisaną powyżej usługę walidacji XML można napisać w Javie, podczas gdy mikrousługę walidacji można jednocześnie napisać w Haskell (aby było to matematycznie rozsądne). Do mikrousługi spedycji ubezpieczeń można użyć Erlanga (bo to naprawdę wymaga skalowania ;)). To, co może wydawać się zabawne z punktu widzenia programisty (tworzenie idealnego systemu przy użyciu doskonałego języka w odizolowanym środowisku), w rzeczywistości nigdy nie jest tym, czego chce organizacja: homogenizacją i standaryzacją. Oznacza to stosunkowo ustandaryzowany zestaw języków, bibliotek i narzędzi, dzięki czemu inni programiści będą mogli w przyszłości nadal wspierać mikrousługę Haskell, gdy przejdziesz na bardziej zielone pastwiska. Przewodnik po mikrousługach Java.  Część 1: Podstawy i architektura mikrousług - 8Historia pokazuje, że standaryzacja jest zwykle zbyt głęboko zakorzeniona. Na przykład programiści w dużych firmach z listy Fortune 500 czasami nie mogli nawet używać Springa, ponieważ „nie stanowiło to części planu technologicznego firmy”. Jednak całkowite przejście do podejścia poliglotycznego to prawie to samo, a druga strona tego samego medalu. Zalecenie: Jeśli zamierzasz używać programowania poliglotycznego, spróbuj mniejszej różnorodności w tym samym ekosystemie języków programowania. Lepiej więc używać razem Kotlina i Javy (oba języki bazują na JVM i są ze sobą w 100% kompatybilne), niż Javy i powiedzmy Haskella. W kolejnej części dowiesz się jak wdrażać i testować mikroserwisy Java.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION