Ignite Service Grid - перезавантаження

26 лютого ми проводили мітап Apache Ignite GreenSource, де виступали контриб'ютери open source проекту Apache Ignite. Важливою подією у житті цієї спільноти стала перебудова компоненту Ignite Service Grid, який дозволяє розгорнути власні мікросервіси прямо в кластері Ignite. Про цей непростий процес на мітапі розповів В'ячеслав Дарадур, програмний інженер і вже понад два роки контриб'ютер Apache Ignite

Ignite Service Grid - перезавантаження

Почнемо із того, що таке Apache Ignite взагалі. Це база даних, яка є розподіленим Key/Value сховище з підтримкою SQL, транзакційності та кешування. Крім того, Ignite дозволяє розгорнути сервіси користувача прямо в кластері Ignite. Розробнику стають доступними всі інструменти, які надає Ignite - розподілені структури даних, Messaging, Streaming, Compute та Data Grid. Наприклад, при використанні Data Grid зникає проблема з адмініструванням окремої інфраструктури під сховище даних і, як наслідок, накладні витрати, що випливають з цього.

Ignite Service Grid - перезавантаження

Використовуючи API Service Grid, можна задеплоїти сервіс, просто вказавши в конфігурації схему деплою і, відповідно, сам сервіс.

Зазвичай схема деплою - це вказівка ​​кількості інстансів, що має бути розгорнуто на вузлах кластера. Є дві типові схеми деплою. Перша - це Cluster Singleton: у будь-який момент часу в кластері гарантовано буде доступний один екземпляр сервісу користувача. Друга - Node Singleton: на кожному вузлі кластера розгорнуто по одному екземпляру сервісу.

Ignite Service Grid - перезавантаження

Також користувач може вказати кількість інстансів сервісу у всьому кластері та визначити предикат для фільтрації відповідних вузлів. За такого сценарію Service Grid сам розрахує оптимальний розподіл для розгортання сервісів.

Крім того, існує така фіча як Affinity Service. Affinity — це функція, що визначає зв'язок ключів із партіціями та зв'язок партій із вузлами в топології. За ключом можна визначити primary-вузол, у якому зберігаються дані. Таким чином можна асоціювати ваш власний сервіс з ключем та кешем affinity-функції. У разі зміни affinity-функції відбудеться автоматичний редеплой. Так сервіс буде завжди розміщений поряд з даними, якими він повинен маніпулювати, і, відповідно, знизити витрати на доступ до інформації. Таку схему можна назвати свого роду колокованими обчисленнями.

Тепер, коли ми розібрали, в чому принадність Service Grid, розповімо про його історію розвитку.

Що було раніше

Попередня реалізація Service Grid базувалася на транзакційному реплікованому системному кеші Ignite. Під словом «кеш» в Ignite мається на увазі сховище. Тобто, це не щось тимчасове, як можна подумати. Незважаючи на те, що кеш реплікується і кожна нода містить весь набір даних, всередині кеш має партійне уявлення. Це з оптимізацією сховищ.

Ignite Service Grid - перезавантаження

Що відбувалося, коли користувач хотів задеплоїти сервіс?

  • Всі вузли у кластері підписувалися на оновлення даних у сховищі за допомогою вбудованого механізму Continuous Query.
  • Нода-ініціатор під read-committed транзакцією робила основу запис, що містила конфігурацію сервісу, включаючи сериализованный инстанс.
  • При отриманні повідомлення про нову запис координатор розраховував розподіл виходячи з конфігурації. Отриманий об'єкт записувався у базу.
  • Якщо вузол входив у розподіл, координатор мав його задеплоїти.

Що нас не влаштовувало

Якогось моменту ми дійшли висновку: так працювати з сервісами не можна. Причин було кілька.

Якщо під час деплою відбувалася якась помилка, то про неї можна було дізнатися лише з ліг того вузла, де все сталося. Існував тільки асинхронний деплой, тому після повернення користувачеві управління від методу деплою потрібно було деякий додатковий час для старту сервісу — і в цей час користувач нічим керувати не міг. Щоб розвивати Service Grid далі, пиляти нові фічі, залучати нових користувачів та робити всім життя простіше, потрібно щось міняти.

При проектуванні нового Service Grid ми насамперед хотіли надати гарантію синхронного деплою: як тільки користувачеві повернулося керування від API, він одразу може користуватися сервісами. Також хотілося дати ініціатору можливість опрацьовувати помилки деплою.

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

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

Проблеми

Які ж глобальні зміни без проблем? Першою стала зміна топології. Потрібно розуміти, що будь-якої миті, навіть у момент деплою сервісу, вузол може увійти в кластер або вийти з нього. Більше того, якщо в момент деплою вузол увійде в кластер, потрібно консистентно передати всю інформацію про послуги в новий вузол. І йдеться не лише про те, що вже було розгорнуто, а й про поточні та майбутні деплої.

Це лише одна із проблем, які можна зібрати в окремий список:

  • Як задеплоїти статично налаштовані послуги при старті вузла?
  • Вихід вузла з кластера – що робити, якщо вузол хостив сервіси?
  • Що робити, якщо змінився координатор?
  • Що робити, якщо клієнт перепідключився до кластера?
  • Чи потрібно обробити запити активації/деактивації та як?
  • А що, коли викликали дестрою кешу, а в нас зав'язані на нього афініті-сервіси?

І це далеко ще не все.

Рішення

Як цільове ми вибрали підхід Event Driven з реалізацією комунікації процесів за допомогою повідомлень. У Ignite вже реалізовано два компоненти, які дозволяють вузлам пересилати повідомлення між собою - communication-spi і discovery-spi.

Ignite Service Grid - перезавантаження

Communication-spi дозволяє нодам безпосередньо спілкуватися та пересилати повідомлення. Він добре підходить для пересилання великого обсягу даних. Discovery-spi дозволяє надіслати повідомлення всім вузлам у кластері. У стандартній реалізації це робиться за топологією «кільце». Також є інтеграція із Zookeeper, у цьому випадку використовується топологія «зірка». Ще варто відзначити важливий момент: discovery-spi надає гарантії того, що повідомлення точно буде доставлено у правильному порядку всім вузлам.

Розглянемо протокол деплою. Всі запити користувача на деплой і роздеплой надсилаються по discovery-spi. Це дає наступні гарантії:

  • Запит буде одержано всіма вузлами у кластері. Це дозволить продовжити обробку запиту при зміні координатора. Також це означає, що за одне повідомлення кожного вузла з'являться всі необхідні метадані, такі як конфігурація сервісу та його серіалізований інстанс.
  • Суворий порядок доставки повідомлень дозволяє вирішувати конфлікти змін та конкуруючих запитів.
  • Так як вхід вузла в топологію обробляється теж по discovery-spi, новий вузол потраплять всі необхідні для роботи з сервісами дані.

При отриманні запиту вузли в кластері валідують і формують завдання на обробку. Ці завдання складаються чергу і потім обробляються у іншому потоці окремим воркером. Це реалізовано таким чином, тому що деплой може займати значний час та затримувати дорогий discovery-потік неприпустимо.

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

  1. Кожен вузол самостійно розраховує розподіл завдяки новій детермінованій assignment-функції.
  2. Вузли формують повідомлення з результатами деплою та відправляють його координатору.
  3. Координатор агрегує всі повідомлення та формує результат всього процесу деплою, який відправляється по discovery-spi всім вузлам у кластері.
  4. При отриманні результату процес деплою завершується, після чого завдання видаляється з черги.

Ignite Service Grid - перезавантаження
Новий event-driven дизайн: org.apache.ignite.internal.processors.service.IgniteServiceProcessor.java

Якщо в момент розгортання відбулася помилка, вузол відразу включає цю помилку в повідомлення, яке направляє координатору. Після агрегації повідомлень координатор матиме інформацію про всі помилки під час деплою та надішле це повідомлення по discovery-spi. Інформація про помилки буде доступна на будь-якому вузлі кластера.

За цим алгоритмом роботи обробляються всі важливі події Service Grid. Наприклад, зміна топології - це теж повідомлення з discovery-spi. І загалом, якщо порівнювати з тим, що було, протокол вийшов досить легким та надійним. Так, щоб обробити будь-яку ситуацію під час деплою.

Що буде далі

Тепер про плани. Будь-яке велике доопрацювання в проекті Ignite виконується як ініціатива на покращення Ignite, так званий IEP. Редизайн Service Grid теж має IEP — IEP №17 зі стебною назвою «Заміна олії в Сервіс Гриді». Але за фактом ми змінили не олію в двигуні, а двигун цілком.

Завдання IEP ми розділили на 2 фази. Перша - велика фаза, яка полягає в переробці протоколу деплою. Вона вже влита в майстер, можна скуштувати новий Service Grid, який з'явиться у версії 2.8. Друга фаза включає безліч інших завдань:

  • Гарячий редеплой
  • Версіонування сервісів
  • Підвищення відмовостійкості
  • Тонкий клієнт
  • Інструменти моніторингу та підрахунку різних метрик

Насамкінець можемо порадити вам Service Grid для побудови відмовостійких систем високої доступності. А також запрошуємо до нас у dev-list и список користувачів поділитись своїм досвідом. Ваш досвід реально важливий для спільноти, він допоможе зрозуміти, куди рухатись далі, як у майбутньому розвивати компонент.

Джерело: habr.com

Додати коментар або відгук