JavaRush /Курси /Модуль 5. Spring /Лекція 203: Як події допомагають роз'єднати мікросервіси

Лекція 203: Як події допомагають роз'єднати мікросервіси

Модуль 5. Spring
Рівень 13 , Лекція 2
Відкрита

Сьогодні ми зануримося в ще більш вузький і цікавий аспект застосування подій і розберемося, як їх використання допомагає роз'єднати мікросервіси, зробити їх автономними і підвищити відмовостійкість.


Мікросервіси й слабка зв'язність: як події змінюють гру

Уявіть, що ваші мікросервіси — це кілька близнюків, які живуть на одній планеті. Кожен ніби живе своїм життям, але внутрішньо вони сильно прив'язані один до одного, тому постійно тягнуть одне одного: "Гей, дай мені дані!", "А ти щось забув?", "Чому я не можу це знайти?". Це сильно гальмує їхнє життя. Щоб близнюки стали більш незалежними й продуктивними, можна впровадити систему повідомлень, де вони просто відправляють свої думки у спільний канал, і інші можуть підхопити їх тільки якщо їм це потрібно. Ця аналогія пояснює головну мету подій: розірвати пряму залежність між компонентами.

Мікросервіси, як ми знаємо, повинні бути слабо пов'язаними (loose coupling). Чим нижча їх залежність один від одного, тим простіше їх розвивати, тестувати і масштабувати. Події служать якраз тим клеєм, який зв'язує компоненти без жорсткої залежності. Замість того, щоб один мікросервіс "кричав" іншому через REST або gRPC, він може просто опублікувати подію, а інші сервіси вже вирішать, чи їх це подія цікавить чи ні.


Приклад: взаємодія мікросервісів через події

Давайте розберемо приклад із реального бізнесу — e-commerce платформи.

Завдання: Управління замовленнями та сповіщеннями

У нас є два мікросервіси:

  1. Order Service — займається створенням замовлення.
  2. Notification Service — відправляє клієнту сповіщення (email, SMS) про статус замовлення.

Традиційний підхід: REST API

Якщо ви проектуєте таку аплікацію звичним способом, Order Service буде безпосередньо викликати Notification Service через HTTP. Це приводить до таких проблем:

  • Якщо Notification Service недоступний (сервер впав, оновлюється), Order Service зависне або поверне помилку.
  • Оновлення Notification Service, наприклад, додавання нового способу сповіщення, вимагатиме змін в Order Service, бо логіка взаємодії між ними закладена жорстко.
  • Масштабування Notification Service стає складнішим, бо залежність між сервісами залишається прямою.

Підхід із подіями

Тепер, якщо Order Service просто публікує подію "OrderCreated" в брокер повідомлень (наприклад, Kafka), Notification Service може підписатися на цю подію і обробити її, коли це можливо. Переваги такого підходу:

  • Ізоляція відповідальності: Order Service більше не переймається тим, що хтось робить з подією. Його задача — лише повідомити світ, що замовлення створено.
  • Помилки більше не блокують: якщо Notification Service не працює, замовлення все одно успішно створиться, а сповіщення буде відправлено пізніше, коли сервіс відновиться.
  • Повторне використання подій: інші сервіси, наприклад Analytics Service, можуть також підписатися на подію "OrderCreated" для підрахунку статистики, без потреби лізти в код Order Service.

Зменшення навантаження на центральні компоненти

Одна з частих проблем у мікросервісній архітектурі — це навантаження на центральні компоненти, такі як API Gateway або база даних. Уявіть, що 100 мікросервісів намагаються одночасно відправляти запити до одного сервера, якщо між ними прямі зв'язки. Події вирішують цю проблему, виступаючи буфером між компонентами.

Брокери повідомлень, такі як Kafka, виступають посередниками, які розподіляють навантаження між паблішерами і сабскрайберами. Якщо один із підписників перевантажений, брокер може утримувати подію доти, поки підписник не буде готовий її обробити. Таким чином, обробка подій стає асинхронною і неблокуючою.


Розподіл відповідальності: основа слабкої зв'язності

Застосовуючи події, ми можемо розділити відповідальність між мікросервісами так, щоб вони ніколи "не бігали один за одним" за отриманням даних або виконанням задач. Розглянемо, як це зробити.

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

  1. Order Service публікує подію "OrderCreated".
  2. Payment Service, підписаний на цю подію, отримує її і починає обробку платежу.
  3. Після успішної обробки платежу, Payment Service публікує подію "PaymentProcessed".
  4. Shipping Service підписується на "PaymentProcessed" і починає процес доставки.

Що ми отримуємо:

  • Повна ізоляція логіки: сервіси знають тільки про події, але не один про одного.
  • Гнучкість змін: ми можемо легко додати новий сервіс, наприклад "LoyaltyPointsService", який отримує подію "OrderCreated" і нараховує користувачу бали за покупку, не чіпаючи код інших мікросервісів.
  • Зменшення крихкості системи: відмова одного сервісу не зачіпає інших (якщо правильно налаштовані черги подій).

Підводні камені та анти-патерни

Хоча подієва архітектура й здається панацеєю, її неправильне використання може привести до хаосу і протилежного ефекту. Ось кілька проблем, які треба враховувати:

  1. Зайві події. Якщо кожен мікросервіс почне публікувати величезну кількість подій, а підписники підпишуться на все підряд, це може привести до лавини даних, які ніхто ніколи не обробить. Наприклад, публікувати подію "UserClickedButton" натискання на будь-яку кнопку сайту — погана ідея.
  2. Складнощі з моніторингом. Легко загубитися в потоці подій. Наприклад, зрозуміти, "чому Shipping Service не запустив доставку після створення замовлення", може бути важко, якщо ви не налаштували логи і трасування.
  3. Залежність від брокерів. Простір для відмови зсувається від сервісів до брокерів. Якщо Kafka вийде з ладу, то жодні повідомлення не будуть доставлені. Це вимагає налаштування кластерів брокерів і резервування.

Рекомендації щодо правильного використання подій

Щоб уникнути проблем і отримати максимум користі від подієвої архітектури, дотримуйтесь цих рекомендацій:

  • Моделюйте події як бізнес-сутності, а не просто технічні повідомлення. Наприклад, "OrderCreated", а не "InsertToDatabase".
  • Використовуйте схеми для опису подій (наприклад, Avro в Kafka), щоб усі сервіси знали структуру і атрибути події.
  • Додавайте метадані до подій, такі як час створення, ідентифікатор транзакції, статус.
  • Налаштовуйте моніторинг брокерів повідомлень: наприклад, використовуйте Prometheus/Grafana для відстеження стану Kafka.

Візуалізація взаємодії мікросервісів через події


+------------------+       "OrderCreated"        +--------------------+
|  Order Service   |--------------------------->|  Notification       |
+------------------+                            |     Service         |
                                                | (Відправлення email)|
"OrderCreated"                                   +--------------------+
         |                           
         |                                                +--------------------+
         ------------------------------------------------>| Analytics Service  |
                                                          | (Збір статистики)  |
                                                          +--------------------+

У цій схемі подія "OrderCreated" публікується Order Service і обробляється двома незалежними сервісами: Notification Service і Analytics Service. Вони використовують одну й ту саму подію, але виконують зовсім різні задачі.


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

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ