JavaRush /Java блог /Random UA /Посібник з мікросервісів Java. Частина 1: основи мікросер...

Посібник з мікросервісів Java. Частина 1: основи мікросервісів та їх архітектура

Стаття з групи Random UA
З цього посібника ви дізнаєтеся що таке мікросервіси Java, як їх проектувати та створювати. Також тут порушені питання про бібліотеки мікросервісів Java та доцільність застосування мікросервісів. Переклад та адаптація Java Microservices: A Practical Guide .

Мікросервіси Java: основи

Щоб зрозуміти мікросервіси, потрібно спочатку визначити, що не є. А чи не є ними “моноліт” — Java monolith: що це таке і які його переваги чи недоліки? Посібник з мікросервісів Java.  Частина 1: основи мікросервісів та їх архітектура.

Що таке Java-моноліт?

Уявіть, що ви працюєте у банку чи фінтех-стартапі. Ви надаєте користувачам мобільну програму, яку можна використовувати для відкриття нового банківського рахунку. У Java коді це призведе до наявності класу-контролера. Спрощено він виглядає так:
@Controller
class BankController {

    @PostMapping("/users/register")
    public void register(RegistrationForm form) {
        validate(form);
        riskCheck(form);
        openBankAccount(form);
        // etc..
    }
}
Вам потрібно, щоб контролер:
  1. Підтверджував форму реєстрації.
  2. Перевіряв ризики на адресау користувача, щоб вирішити, чи надавати йому банківський рахунок.
  3. Відкривав банківський рахунок.
Клас BankControllerбуде упакований разом з рештою ваших вихідних файлів bank.jar або bank.war для розгортання - це і є старий добрий моноліт, що містить весь код, необхідний для роботи вашого банку. За грубими підрахунками, початковий розмір .jar (або .war) файлу складе від 1 до 100 МБ. Тепер ви можете просто запустити .jar файл на своєму сервері… і це все, що потрібно зробити для розгортання Java-додатки. Посібник з мікросервісів Java.  Частина 1: основи мікросервісів та їх архітектура.Зображення, лівий прямокутник зверху: розгортання моно(літного) банку java -jar bank.jar (cp .war/.ear into appserver). Правий прямокутник: відкритий браузер.

У чому проблема з Java-монолітами?

По суті, в Java-монолітах немає нічого поганого. Однак досвід показав, що якщо у вас у проекті:
  • Працює багато програмістів / команд / консультантів.
  • ...над одним і тим же монолітом під тиском замовників з вельми невиразними вимогами...
  • протягом кількох років...
… то в такому разі ваш маленький файлик bank.jar перетворюється на неозорий гігабайт одного тільки коду, до якого навіть підступитися страшно, не кажучи вже про розгортання.

Як зменшити розмір моноліту Java?

Виникає природне питання: як зробити моноліт менше? Зараз ваш bank.jar працює на одній JVM, один процес на одному сервері. Ні більше, ні менше. І ось саме зараз на думку може спасти логічна думка: «Але ж служба перевірки ризиків може використовуватися й іншими відділами в моїй компанії! Вона не має безпосереднього відношення до моєї монолітної банківської програми! Можливо, чи варто вирізати її з моноліту і розгорнути, як окремий продукт? Тобто якщо говорити технічно, запустити його як окремий процес Java”.

Що таке мікросервіс Java?

На практиці таке словосполучення означає, що тепер виклик методу riskCheck()здійснюватиметься не з BankController: цей метод або bean-компонент з усіма його допоміжними класами буде переміщений у власний Maven- або Gradle-проект. Також його буде розгорнуто та поміщено під систему контролю версій незалежно від банківського моноліту. Однак весь цей процес вилучення не перетворює ваш новий модуль RiskCheck на мікросервіс як такий, оскільки визначення мікросервісу відкрито для інтерпретації. Це призводить до частих дискусій усередині команд та компаній.
  • 5-7 класів у проекті – це мікро чи як?
  • 100 чи 1000 класів... все ще мікро?
  • Мікросервіс взагалі пов'язаний із кількістю класів чи ні?
Давайте залишимо теоретичні міркування, а замість них дотримуватимемося прагматичних міркувань і зробимо ось що:
  1. Назвемо всі сервіси, що окремо розгортаються, мікросервісами, незалежно від їх розмірів або меж домену.
  2. Подумаємо, як влаштувати міжсервісне спілкування. Нашим мікросервісам потрібні способи спілкування.
Отже, підіб'ємо підсумки: раніше у вас був один JVM-процес, цілісний моноліт для роботи банку. Тепер у вас є JVM-процес банківського моноліту та окремий мікросервіс RiskCheck, який працює в рамках власного JVM-процесу. І тепер для перевірки ризиків ваш моноліт має викликати цей мікросервіс. Як це зробити?

Як налагодити комунікацію між мікросервісами Java?

Загалом є два варіанти — синхронна та асинхронна комунікація.

Синхронна комунікація: (HTTP)/REST

Зазвичай синхронізована комунікація між мікросервісами здійснюється через HTTP та REST-подібні сервіси, які повертають XML чи JSON. Зрозуміло, можуть бути й інші варіанти взяти хоча б Google Protocol Buffers . Якщо вам потрібна негайна відповідь, краще використовувати REST-комунікацію. У нашому прикладі саме так і потрібно робити, оскільки перевірка ризиків є обов'язковою перед відкриттям рахунку. Якщо немає перевірки ризиків, немає і рахунку. Інструменти обговоримо нижче, у розділі “ Які бібліотеки найкраще підходять для синхронних викликів Java REST ”.

Обмін повідомленнями – асинхронна комунікація

Асинхронний мікросервісний зв'язок зазвичай здійснюється за допомогою обміну повідомленнями з реалізацією JMS та/або за допомогою протоколу, наприклад, AMQP . Ми тут написали "зазвичай" не просто так: скажімо, кількість інтеграцій електронною поштою/SMTP не можна недооцінювати. Використовуйте його тоді, коли вам не потрібна негайна відповідь. Наприклад, користувач натискає кнопку "купити зараз", а ви у свою чергу хочете згенерувати рахунок-фактуру. Цей процес, безумовно, не повинен відбуватися в рамках циклу запиту-відповіді користувача на покупку. Нижче описано, які інструменти найкраще підходять для асинхронного обміну повідомленнями Java .

Приклад: виклик REST API у Java

Припустимо, ми обрали синхронну мікросервісну комунікацію. У такому разі наш Java-код (той, що ми наводабо вище) на низькому рівні виглядатиме приблизно так. (Під низьким рівнем тут ми розуміємо той факт, що для мікросервісної комунікації зазвичай створюються клієнтські бібліотеки, які абстрагують вас від реальних 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..
    }
}
Виходячи з коду, стає ясно, що тепер нам потрібно розгорнути два Java-(мікро) сервіси, Bank та RiskCheck. У результаті у нас буде запущено два JVM-процеси. Посібник з мікросервісів Java.  Частина 1: основи мікросервісів та їх архітектура.Ось і все, що вам потрібно для розробки проекту з мікросервісами Java: просто створюйте та розгортайте менші фрагменти (файли .jar або .war) замість одного монолітного. Залишається незрозумілою відповідь на питання, яким чином нам слід розрізати моноліт на мікросервіси? Наскільки дрібними мають бути ці шматочки, як визначити правильний розмір? Давайте перевіримо.

Архітектура Java Microservices

Насправді компанії розробляють мікросервісні проекти по-різному. Підхід залежить від того, чи намагаєтеся ви перетворити існуючий моноліт на проект із мікросервісами або ж починаєте проект з нуля.

Від моноліту до мікросервісів

Одна з найлогічніших ідей - витягти мікросервіси з існуючого моноліту. Зверніть увагу, що приставка «мікро» тут насправді не означає, що вилучені сервіси будуть невеликими, це зовсім не обов'язково. Розгляньмо теоретичні основи.

Ідея: розбити моноліт на мікросервіси

До легасі-проектів можна застосувати мікросервісний підхід. І ось чому:
  1. Найчастіше такі проекти складно підтримувати/змінювати/розширювати.
  2. Усі, починаючи від розробників і до керівництва, хочуть спрощення.
  3. У вас (щодо) чіткі межі домену, тобто ви знаєте, що саме має робити ваше програмне забезпечення.
Повертаючись до нашого прикладу, це означає, що ви можете поглянути на свій банківський Java-моноліт і спробувати розбити його за межами домену.
  • Так, буде розумно виділити в окремий мікросервіс «Управління обліковими записами», обробку даних користувача (таких як імена, адресаи, номери телефонів).
  • Або вищезгаданий «Модуль перевірки ризиків, який перевіряє рівні ризику користувача та може використовуватись багатьма іншими проектами або навіть відділами компанії.
  • Або модуль виставлення рахунків, який надсилає рахунки у форматі PDF або поштою.

Втілення ідеї: нехай це зробить хтось інший

Описаний вище підхід відмінно виглядає на папері та UML-подібних діаграмах. Однак, все не так просто. Для його практичної реалізації потрібна серйозна технічна підготовка: прірва між розумінням того, що було б непогано витягти з моноліту і процесом вилучення — величезна. Більшість корпоративних проектів сягають стадії, коли розробники побоюються, скажімо, оновити 7-річну версію Hibernate до новішої. Разом з нею оновляться бібліотеки, проте існує неілюзорна небезпека щось поламати. І ось, ті ж розробники тепер повинні копатися в стародавньому легасі-коді з незрозумілими межами транзакцій бази даних і отримувати чітко визначені мікросервіси? Найчастіше це завдання дуже складна, її не вийде "вирішити" на дошці або на нарадах з архітектури. Посібник з мікросервісів Java.  Частина 1: основи мікросервісів та їх архітектура.Процитую Twitter розробника @simonbrown: Я повторюватиму це знову і знову ... якщо люди не можуть правильно будувати моноліти, мікросервіси не допоможуть. Саймон Браун

Проект "з нуля" на базі мікросервісної архітектури

У разі нових Java-проектів три нумеровані пункти з попередньої частини виглядають дещо інакше:
  1. Ви починаєте з чистого листа, тому немає “багажу” для обслуговування.
  2. Розробники хотіли б, щоб у майбутньому все було просто.
  3. Проблема: у вас набагато туманніша картина меж доменів: ви не знаєте, що насправді має робити ваше програмне забезпечення (підказка: agile;))
Це призводить до того, що компанії намагаються використати нові проекти з мікросервісами Java.

Технічна мікросервісна архітектура

Перший пункт здається найбільш очевидним для розробників, однак є й ті, хто вкрай не рекомендує його. Хаді Харірі рекомендує рефакторинг "Extract Microservice" в IntelliJ. І хоча наступний приклад дуже спрощений, реалізації, які спостерігаються в реальних проектах, на жаль, не надто далеко від нього пішли. До мікросервісів
@Service
class UserService {

    public void register(User user) {
        String email = user.getEmail();
        String username =  email.substring(0, email.indexOf("@"));
        // ...
    }
}
З substring-мікросервісом 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());
        // ...
    }
}
Таким чином, ви, по суті, включає виклик Java в виклик HTTP, без очевидних причин для цього. Одна з причин, однак, полягає у наступному: відсутність досвіду та спроба форсувати підхід на основі мікросервісів Java. Рекомендація: не робіть цього.

Workflow-орієнтована мікросервісна архітектура

p align="justify"> Наступним поширеним підходом є поділ мікросервісів Java на модулі на базі workflow. Приклад із реального життя: у Німеччині, коли ви звертаєтесь до (загальнодоступного) лікаря, він має записати ваше відвідування у своїй медичній CRM-системі. Щоб отримати оплату від страховки, він надішле дані про ваше лікування (і лікування інших пацієнтів) посереднику через XML. Посередник розгляне цей файл XML і (спрощено):
  1. Перевірить, чи правильний XML-файл отримано.
  2. Перевірить правдоподібність процедур: скажімо, однорічна дитина, яка отримала три процедури чищення зубів за один день від лікаря-гінеколога виглядає дещо підозріло.
  3. Об'єднає XML із деякими іншими бюрократичними даними.
  4. Перешле XML-файл до страхової компанії, щоб ініціювати платежі.
  5. І перешле результат лікаря, забезпечивши його повідомленням «успіх» або «будь ласка, надішліть цей запис ще раз, як тільки це матиме сенс».
Примітка. У цьому прикладі комунікація між мікросервісами не відіграє ролі, але цілком може бути виконана асинхронно брокером повідомлень (наприклад, RabbitMQ), оскільки лікар все одно не отримує негайного зворотного зв'язку. Посібник з мікросервісів Java.  Частина 1: основи мікросервісів та їх архітектура.Знову ж таки, це чудово виглядає на папері, але виникають закономірні питання:
  • Чи є потреба у розгортанні шести додатків для обробки одного XML-файлу?
  • Чи ці мікросервіси незалежні один від одного? Чи можуть вони бути розгорнуті незалежно один від одного? З різними версіями та схемами API?
  • Що робить мікросервіс правдоподібності інформації, якщо мікросервіс перевірки не працює? Система все ще працює?
  • Чи поділяють ці мікросервіси одну й ту саму базу даних (їм, безумовно, потрібні деякі загальні дані в таблицях БД), чи кожен має свою власну?
  • … і багато іншого.
Цікаво, що наведена вище діаграма виглядає простіше, тому що кожен сервіс тепер має своє точне, чітко визначене призначення. Раніше це виглядало приблизно як цей страшний моноліт: Посібник з мікросервісів Java.  Частина 1: основи мікросервісів та їх архітектура.Незважаючи на те, що можна сперечатися про простоту цих діаграм, тепер вам безперечно потрібно вирішити ці додаткові операційні завдання.
  • Вам потрібно не просто розгорнути одну програму, а як мінімум шість.
  • Можливо, навіть потрібно розгорнути кілька баз даних, залежно від того, як далеко ви хочете заглибитись у мікросервісну архітектуру.
  • Потрібно стежити, щоб кожна система працювала в режимі онлайн, причому працювала нормально.
  • Необхідно переконатися, що ваші виклики між мікросервісами дійсно стійкі (див. Як зробити мікросервіс Java стійким?).
  • І все інше, що має на увазі це налаштування — від локальних налаштувань розробки до інтеграційного тестування.
Тож рекомендація буде наступною:
  • Якщо ви не Netflix (скоріше за все, ви не Netflix) ...
  • Якщо ви не маєте надсильних навичок роботи, при яких ви відкриваєте середовище розробки, а воно викликає хаосну мавпу, яка відкидає вашу виробничу базу даних, яка легко відновлюється через 5 секунд.
  • або ви почуваєтеся як @monzo і готові випробувати 1500 мікросервісів просто тому, що можете.
→ Не робіть цього. А тепер менш гіперболізовано. Спроба моделювати мікросервіс після доменних кордонів здається цілком розумною. Але це не означає, що потрібно взяти один робочий процес та розділити його на крихітні окремі частини (отримати XML, перевірити XML, надіслати XML). Отже, кожного разу, коли ви починаєте новий проект з мікросервісів Java, і межі домену все ще дуже розпливчасті, намагайтеся підтримувати розмір мікросервісів на нижньому рівні. Пізніше ви завжди зможете додати більше модулів. І переконайтеся, що у вас знайдеться розвинений DevOps у команді/компанії/підрозділі для підтримки нової інфраструктури.

Поліглот або командно-орієнтована мікросервісна архітектура

Існує третій, майже лібертаріанський, підхід до розробки мікросервісів: надання командам або навіть окремим особам можливості реалізовувати власні історії із застосуванням будь-якої кількості мов або мікросервісів (маркетологи називають такий підхід "поліглотським програмуванням"). Так, описана вище служба перевірки XML може бути написана на Java, а мікросервіс валідації в той же час – мовою Haskell (щоб зробити його математично обґрунтованим). Для мікросервісу пересилання страховки можна застосувати мову Erlang (бо він дійсно повинен масштабуватися;)). Те, що може здатися кумедним з погляду розробника (розробка ідеальної системи з вашою ідеальною мовою в ізольованому середовищі), по суті, ніколи не є тим, чого хоче організація: гомогенізація та стандартизація. Посібник з мікросервісів Java.  Частина 1: основи мікросервісів та їх архітектура.Історія показує, що зазвичай стандартизація укорінюється надто глибоко. Скажімо, розробникам великих компаній зі списку Fortune 500 іноді навіть не дозволяли використовувати Spring, оскільки це «не входить у план компанії з технологій». Втім, повний перехід на підхід поліглоту — це майже те саме, інша сторона тієї ж монети. Рекомендація: якщо ви збираєтеся використовувати поліглот-програмування, спробуйте менше різноманіття в одній екосистемі мови програмування. Так, краще застосовувати разом Kotlin і Java (обидві мови засновані на JVM і 100% сумісні один з одним), а не Java і, скажімо, Haskell. У наступній частині ви дізнаєтеся про розгортання та тестування мікросервісів Java.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ