Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Що може змусити таку велику компанію як Lamoda з налагодженим процесом та десятками взаємопов'язаних сервісів суттєво змінювати підхід? Мотивація може бути різна: від законодавчої до властивого всім програмістам бажання експериментувати.

Але це зовсім не означає, що не можна розраховувати на додатковий зиск. У чому можна виграти, якщо впровадити events-driven API на Kafka, розповість Сергій Заїка (fewald). Про набиті гулі та цікаві відкриття теж обов'язково буде — не може експеримент без них обійтися.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Disclaimer: Ця стаття ґрунтується на матеріалах мітапу, який Сергій провів у листопаді 2018 року на HighLoad++. Живий досвід Lamoda роботи з Kafka залучив слухачів не менше ніж на інші доповіді з розкладу. Нам здається, це відмінний приклад того, що завжди можна і потрібно знаходити однодумців, а організатори HighLoad++ і надалі намагатимуться створювати атмосферу, що сприяє цьому.

про процес

Lamoda - це велика e-commerce платформа, яка має свій контакт-центр, служба доставки (і безліч партнерських), фотостудія, величезний склад і все це працює на своєму софті. Є десятки способів оплати, b2b-партнери, які можуть користуватися частиною або всіма цими послугами та хочуть знати актуальну інформацію щодо своїх товарів. До того ж, Lamoda працює в трьох країнах крім РФ і там все трохи по-своєму. Разом існує, напевно, більше сотні способів налаштувати нове замовлення, яке має бути по-своєму оброблене. Все це працює за допомогою десятків сервісів, які іноді спілкуються неочевидним чином. Ще є центральна система, чия головна відповідальність – це статуси замовлень. Ми називаємо її BOB, я працюю з нею.

Refund Tool with events-driven API

Слово events-driven досить заїжджене, трохи далі докладніше визначимо, що розуміється. Почну з контексту, в якому ми вирішили випробувати підхід events-driven API у Kafka.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

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

Але повернення ускладнилося через зміну законодавства, і нам довелося реалізувати під нього окремий мікросервіс.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Наша мотивація:

  1. Закон ФЗ-54 — коротко, закон вимагає повідомляти в податкову про кожну грошову операцію, чи то повернення, чи прихід, у досить короткий SLA за кілька хвилин. Ми, як e-commerce, проводимо чимало операцій. Технічно це означає нову відповідальність (а отже новий сервіс) та доопрацювання у всіх причетних системах.
  2. BOB split - Внутрішній проект компанії з позбавлення BOB від великої кількості непрофільних відповідальності і зниження його загальної складності.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

На цій схемі намальовано основні системи Lamoda. Зараз більшість з них є швидше сузір'я з 5-10 мікросервісів навколо моноліту, що зменшується.. Вони потихеньку ростуть, але ми намагаємося робити їх менше, бо деплоїти виділений у середині фрагмент страшно — не можна допустити, щоб він упав. Всі обміни (стрілочки) ми змушені резервувати та закладатися на те, що будь-який з них може виявитися недоступним.

У BOB також досить багато обмінів: системи оплати, доставки, нотифікації тощо.

Технічно BOB це:

  • ~150k рядків коду + ~100k рядків тестів;
  • php7.2 + Zend 1 & Symfony Components 3;
  • >100 API & ~50 вихідних інтеграцій;
  • 4 країни зі своєю бізнес-логікою.

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

Процес повернення

Спочатку процес залучено дві системи: BOB і Payment. Тепер з'являються ще дві:

  • Fiscalization Service, який візьме на себе проблеми з фіскалізацією та спілкування із зовнішніми сервісами.
  • Refund Tool, який просто виносяться нові обміни, щоб не роздмухувати BOB.

Тепер процес виглядає так:

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

  1. До BOB надходить запит на повернення грошей.
  2. BOB говорить про це Refund Tool.
  3. Refund Tool говорить Payment: "Поверни гроші".
  4. Payment повертає гроші.
  5. Refund Tool і BOB синхронізують між собою статуси, бо поки що їм обом це потрібно. Ми поки не готові повністю перейти в Refund Tool, оскільки в BOB є UI, звіти для бухгалтерії, і взагалі багато даних, які так просто не перенесеш. Доводиться сидіти на двох стільцях.
  6. Залишається запит на фіскалізацію.

У результаті ми зробили на Kafka якусь шину подій - event-bus, де все зав'язалося. Ура, тепер ми маємо єдину точку відмови (сарказм).

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Плюси та мінуси досить очевидні. Ми зробили шину, отже тепер всі сервіси від неї залежать. Це спрощує проектування, але вносить у систему єдину точку відмови. Впаде kafka, процес встане.

Що таке events-driven API

Хороша відповідь на це запитання є у доповіді Мартіна Фаулера (GOTO 2017) "Багато Meanings of Event-Driven Architecture".

Коротко, що ми зробили:

  1. Загорнули всі асинхронні обміни через events storage. Замість того, щоб повідомляти через мережу кожному зацікавленому споживачеві про зміну статусу, ми пишемо в централізоване сховище подію про зміну стану, а зацікавлені в топі споживачі читають звідти все, що з'являється.
  2. Подія (event) у даному випадку – це повідомлення (Повідомлення) про те, що щось десь змінилося. Наприклад, змінився статус замовлення. Споживач, якому важливі якісь супровідні зміни статусу дані і яких немає в повідомленні, може дізнатися про їх стан сам.
  3. Максимальний варіант - повноцінний event sourcing, state transfer, При якому event містить всю інформацію, необхідну для обробки: звідки і в який статус перейшли, як саме змінилися дані та ін.

У рамках запуску Refund Tool ми використали третій варіант. Це спростило обробку подій, оскільки не потрібно видобувати детальну інформацію, плюс виключило сценарій, коли кожна нова подія породжує сплеск уточнюючих get-запитів від споживачів.

Сервіс Refund Tool не навантаженийтому Kafka там швидше проба пера, ніж необхідність. Не думаю, що, якби сервіс повернення коштів став highload-проектом, бізнес був би радий.

Async exchange AS IS

Для асинхронних обмінів PHP департамент зазвичай використовує RabbitMQ. Зібрали дані для запиту, поклали в чергу, і консьюмер цього ж сервісу його рахував та відправив (або не відправив). Для API Lamoda активно використовує Swagger. Проектуємо API, описуємо його в Swagger, генеруємо клієнтський та серверний код. Ще ми використовуємо трохи розширений JSON RPC 2.0.

Де-не-де використовуються esb-шини, хтось живе на activeMQ, але, в цілому, RabbitMQ - стандарт.

Async exchange TO BE

Проектуючи обмін через events-bus, простежується аналогія. Ми схожим чином описуємо майбутній обмін даними через опис структури event'а. Формат yaml, кодогенерацію довелося робити самим, генератор специфікації створює DTO і вчить клієнти та сервери з ними працювати. Генерація йде двома мовами. golang та php. Це дозволяє тримати бібліотеки узгодженими. Генератор написаний на golang, за що отримав ім'я gogi.

Event-sourcing на Kafka – річ типова. Є рішення від головної enterprise версії Kafka Confluent, є nakadi, рішення від наших «братів» щодо доменної області Zalando Наша мотивація почати з vanilla Kafka — це залишити рішення безкоштовним, поки остаточно не вирішили повсюдно його використовувати, а також залишити собі простір для маневру та доробок: ми хочемо підтримку свого JSON RPC 2.0, генератори під дві мови та подивимося що ще.

Іронічно, що навіть у такому щасливому випадку, коли є приблизно аналогічний бізнес Zalando, який зробив приблизно схоже рішення, ми не можемо ефективно його використовувати.

Архітектурно на запуску патерн такий: читаємо безпосередньо з Kafka, але пишемо лише через events-bus. Для читання в Kafka багато готового: брокери, балансувальники і вона більш-менш готова під масштабування по горизонталі, це хотілося зберегти. Запис же ми захотіли загорнути через один Gateway aka Events-bus, і ось чому.

Events-bus

Або автобус подій. Це просто stateless http gateway, який бере на себе кілька важливих ролей:

  • Валідація прод'юсингу - Перевіряємо, що події відповідають нашій специфікації.
  • Майстер-система з подій, тобто це головна та єдина система в компанії, яка відповідає на питання, які ж events з якими структурами вважаються валідними. У валідацію входять просто типи даних та enums для жорсткої специфікації вмісту.
  • Hash-функція для шардування — структура повідомлення Kafka це key-value і по хешу від key обчислюється, куди це класти.

Чому

Ми працюємо у великій компанії з налагодженим процесом. Навіщо щось міняти? Це експеримент, і ми сподіваємося отримати кілька вигод.

1:n+1 обміни (один до багатьох)

З Kafka дуже просто підключати до API нових споживачів.

Допустимо у вас є довідник, який потрібно тримати актуальним у кількох системах одразу (і в якихось нових). Раніше ми винаходили bundle, що реалізовував set-API, а майстер-системі повідомляли адреси споживачів. Тепер майстер-система шле оновлення до топіка, а всі, кому цікаво читають. З'явилася нова система – підписали її на топік. Так, теж bundle, але простіше.

У випадку з refund-tool, який є шматочком BOB, нам зручно через Kafka тримати їх синхронізованими. Payment каже, що гроші повернули: BOB, RT про це дізналися, змінили статуси, Fiscalization Service про це дізнався і вибив чек.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

У нас є плани зробити єдиний Notifications Service, який би сповіщав клієнта про новини на його замовлення/повернення. Наразі ця відповідальність розмазана між системами. Нам достатньо навчити Notifications Service виловлювати з Kafka релевантну інформацію та реагувати на неї (і відключити в інших системах ці повідомлення). Жодних нових прямих обмінів не потрібно.

На основі даних

Інформація між системами стає прозорою - який би «кривавий enterprise» у вас не стояв і яким би пухким не був ваш backlog. У Lamoda є відділ Data Analytics, який збирає дані по системах і наводить їх у вид, як для бізнесу, так і для інтелектуальних систем. Kafka дозволяє швидко дати їм багато даних та тримати цей інформаційний потік актуальним.

Replication log

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

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

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Далі трохи переказу документації, для тих хто з Kafka не знайомий (картинка теж із документації)

У AMQP є черги: пишемо повідомлення у чергу для консьюмера. Як правило, одну чергу обробляє одна система з однією і тією самою бізнес-логікою. Якщо потрібно сповістити кілька систем, можна навчити програму писати в кілька черг або налаштувати exchange з механізмом fanout, який сам їх клонує.

У Kafka є схожа абстракція тема, в яку ви пишіть повідомлення, але вони не зникають після прочитання. За замовчуванням, при підключенні до Kafka ви отримуєте всі повідомлення, і при цьому є можливість зберегти місце, на якому ви зупинилися. Тобто, ви читаєте послідовно, можете не відзначити повідомлення прочитаним, але зберегти id, з якого потім продовжите читання. Id, на якому ви зупинилися, називається offset (зміщення), а механізм – commit offset.

Відповідно, можна реалізувати різну логіку. Наприклад, у нас BOB існує у 4 інстансах для різних країн – Lamoda є в Росії, Казахстані, Україні, Білорусії. Оскільки вони деплояться окремо, у них трохи свої конфіги та своя бізнес-логіка. Ми вказуємо у повідомленні, до якої країни воно належить. Кожен консьюмер BOB у кожній країні читає з різними групами,і, якщо повідомлення до нього не належить, пропускають його, тобто. відразу комітіт offset +1. Якщо той же топік читає наш Payment Service, він робить це з окремою групою, і тому offset не перетинаються.

Вимоги до подій:

  • Повнота даних. Хотілося б, щоб у події було достатньо даних, щоб її можна було обробити.

  • Цілісність. Ми делегуємо Events-bus перевірку того, що консистентна подія і він може її обробити.
  • Порядок важливий. У разі повернення ми змушені працювати з історією. З нотифікаціями порядок не важливий, якщо це однорідні нотифікації, email буде однаковий незалежно від того, яке замовлення прибуло першим. У разі повернення є чіткий процес, якщо змінити порядок, то виникнуть винятки, refund не створиться чи не обробиться – ми потрапимо до іншого статусу.
  • Узгодженість. Ми маємо сховище, і тепер ми замість API створюємо events. Нам потрібен спосіб, швидко і дешево передавати в наші сервіси інформацію про нові events та про зміни вже існуючих. Це досягається за допомогою загальної специфікації в окремому git-репозиторії та кодогенераторах. Тому клієнти та сервери у різних сервісах у нас узгоджені.

Kafka в Lamoda

У нас є три установки Kafka:

  1. Logs;
  2. R&D;
  3. Events-bus.

Сьогодні ми говоримо лише про останній пункт. У events-bus у нас не дуже великі інсталяції — 3 брокери (сервери) та всього 27 топиків. Як правило, один топік – це один процес. Але це тонкий момент, і зараз ми його торкнемося.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Вище графік rps. Процес refunds позначений бірюзовою лінією (так, той, що лежить на осі X), а рожевим - процес оновлення контенту.

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

Рожеві піки - це product update, тобто зміни товарів. Видно, що хлопці фотографували, фотографували, а потім вкотре! - Завантажили пачку подій.

Lamoda Events use cases

Побудовану архітектуру ми використовуємо для таких операцій:

  • Відстеження статусів повернень: call-to-action та трекінг статусів від усіх залучених систем Оплата, статуси, фіскалізація, нотифікація. Тут ми випробували підхід, зробили інструменти, зібрали всі баги, написали документацію та розповіли колегам, як цим користуватися.
  • Оновлення карток товару: конфігурація, мета-дані, показники. Читає одна система (яка відображає), а пишуть кілька.
  • Email, push та sms: замовлення зібралося, замовлення доїхало, повернення прийнято і т.д., багато їх.
  • Сток, оновлення складу - Кількісне оновлення найменувань, просто числа: надходження на склад, повернення. Потрібно, щоб усі системи, пов'язані з резервуванням товару, оперували максимально актуальними даними. Зараз система оновлення стоку досить складна, Kafka дозволить її спростити.
  • Аналіз даних (R&D-відділ), ML-інструменти, аналітика, статистика. Ми хочемо, щоб інформація була прозорою — для цього Kafka добре підходить.

Тепер цікавіша частина про набиті гулі та цікаві відкриття, які відбулися за півроку.

Проблеми проектування

Припустимо, ми хочемо зробити нову штуку – наприклад, перевести на Kafka весь процес доставки. Зараз частина процесу реалізується в Order Processing у BOB. За передачею замовлення до служби доставки, переміщенням на проміжний склад та іншим стоїть статусна модель. Є цілий моноліт, навіть два, плюс купа API, присвячених доставці. Вони знають про доставку набагато більше.

Здається, що це схожі області, але для Order Processing у BOB та для системи доставки статуси відрізняються. Наприклад, деякі кур'єрські служби не надсилають проміжні статуси, а лише фінальні: «доставили» чи «втратили». Інші, навпаки, докладно повідомляють про переміщення товару. У всіх свої правила валідації: для когось email валідний, отже, його опрацюють; для інших — не валідний, але замовлення все одно буде оброблено, бо є телефон для зв'язку, а хтось скаже, що таке замовлення взагалі не оброблятиме.

Потік даних

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

В один топік чи в різні?

Ми маємо специфікацію події. В BOB ми пишемо, що таке замовлення треба доставити, і вказуємо: номер замовлення, його склад, якісь SKU і бар-коди і т.д. Коли товар прибуде на склад, доставка зможе одержати статуси, часиповітря і все, що потрібно. Але далі ми хочемо у BOB отримувати оновлення за цими даними. У нас виникає зворотний процес отримання даних із доставки. Це та сама подія? Чи це окремий обмін, який заслуговує на окремий топік?

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

Нове поле чи нову подію?

Але якщо використовувати ті самі події, виникає інша проблема. Наприклад, не всі системи доставки можуть згенерувати DTO, яке зможе генерувати BOB. Ми відправляємо їм id, а вони їх не зберігають, бо вони не потрібні, а з точки зору старту процесу event-bus це поле обов'язкове.

Якщо ми вводимо для event-bus правило, що це поле є обов'язковим, то змушені в BOB або в обробнику стартової події ставити додаткові правила валідації. Валідація починає розповзатись по сервісу — це не дуже зручно.

Ще одна проблема – це спокуса інкрементальної розробки. Нам кажуть, що треба щось додати до події, і, можливо, якщо добре подумати, це мала бути окрема подія. Але в нашій схемі окрема подія – це окремий топік. Окремий топік - це весь той процес, який я описав вище. У розробника виникає спокуса просто внести в JSON схему ще одне поле та перегенерувати.

У випадку refunds ми так протягом півроку прибули до події подій. У нас була одна мета-подія, яка називається refund update, в якій було поле type, що описують, у чому власне цей update полягає. Від цього ми мали «прекрасні» свічі з валідаторами, які говорили, як треба валідувати цю подію з цим type.

Версіонування подій

Для валідації повідомлень у Kafka можна використовувати AvroАле потрібно було відразу закладати на це і використовувати Confluent. У нашому випадку із версіонуванням доводиться бути обережним. Не завжди можна буде перечитати повідомлення з replication log, тому що модель «поїхала». В основному, виходить будувати версії так, щоб модель була обернено сумісною: наприклад, зробити поле тимчасово необов'язковим. Якщо відмінності надто сильні, починаємо писати в новий топік, а клієнти пересаджуємо, коли вони дочитають старий.

Гарантія порядку читання partitions

Топики всередині Kafka розбиті на partitions. Це не дуже важливо, поки ми проектуємо сутності та обміни, але важливо, коли вирішуємо, як це консьюміти і масштабувати.

У звичайному випадку ви пишіть у Kafka один топік. За замовчуванням використовується один partition, і всі повідомлення цього топіка потрапляють до нього. А консьюмер відповідно читає ці повідомлення. Припустимо, тепер потрібно розширити систему так, щоб повідомлення читали два різних консьюмери. Якщо ви, наприклад, відправляєте СМС, то можна сказати Kafka зробити додатковий partition і Kafka почне розкладати повідомлення на дві частини — половину туди, половину сюди.

Як Kafka їх ділить? Кожне повідомлення має тіло (в якому ми зберігаємо JSON) і є key. До цього ключа можна додати хеш-функцію, яка визначатиме, до якого partition потрапить повідомлення.

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

Events vs commands

Це ще одна проблема, з якою ми зіткнулися. Event — це певна подія: ми говоримо, що десь сталося (something_happened), наприклад, item скасувався або стався refund. Якщо ці події хтось слухає, то за «item скасувався» сутність refund буде створена, а «відбувся refund» запишеться десь у сетапах.

Але зазвичай, коли ви проектуєте події, ви ж не хочете писати їх дарма — ви закладаєтеся на те, що їх хтось читатиме. Висока спокуса написати не something_happened (item_canceled, refund_refunded), а something_should_be_done. Наприклад, item готовий до повернення.

З одного боку, це нагадує, як подія буде використана. З іншого боку, це набагато менше схоже на нормальну назву події. До того ж, звідси вже недалеко до команди do_something. Але у вас немає гарантії, що цю подію хтось прочитав; а якщо прочитав, то прочитав успішно; а якщо прочитав успішно, то зробив щось, і це пройшло успішно. У той момент, коли подія стає do_something, стає необхідним зворотний зв'язок, і це проблема.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

В асинхронному обміні в RabbitMQ, коли ви прочитали повідомлення, сходили до http, у вас є response - хоча б, що повідомлення було прийнято. Коли ви записали Kafka, є повідомлення, що ви записали Kafka, але про те, як воно обробилося, ви нічого не знаєте.

Тому в нашому випадку довелося вводити подію у відповідь і налаштовувати моніторинг на те, що якщо вилетіло стільки подій, через такий час має надійти стільки ж подій у відповідь. Якщо цього не сталося, то здається щось пішло не так. Наприклад, якщо ми відправили подію "item_ready_to_refund", ми очікуємо, що refund створиться, клієнту повернуться гроші, нам вилетить подія "money_refunded". Але це не точно, тому потрібний моніторинг.

Нюанси

Є досить очевидна проблема: якщо ви читаєте з топіка послідовно, і у вас якесь повідомлення погане, консьюмер падає, і далі ви не підете. Вам потрібно зупинити всі консьюмери, комітити offset далі, щоб продовжити читання.

Ми про це знали, на це заклалися і все одно це сталося. А сталося це тому, що подія була валідною з точки зору events-bus, подія була валідною з погляду валідатора програми, але вона не була валідною з точки зору PostgreSQL, тому що у нас в одній системі MySQL з UNSIGNED INT, а в свіжонаписаній Система була PostgreSQL просто з INT. У нього розмір трохи менший, і Id не вмістився. Symfony помер з винятком. Ми, звичайно, виняток упіймали, тому що заклалися на нього, і збиралися комітити цей offset, але перед цим хотіли інкрементувати лічильник проблем, якщо повідомлення обробилося невдало. Лічильники у цьому проекті теж лежать у базі, а Symfony вже закрив спілкування з базою, і другий виняток вбив весь процес без шансів коммітувати offset.

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

Kafka має можливість через tooling поставити довільний offset. Але щоб це зробити, потрібно зупинити всі консьюмери - у нашому випадку приготувати окремий реліз, в якому не буде консьюмерів, redeployments. Тоді у Kafka через tooling можна змістити offset і повідомлення пройде.

Інший нюанс replication log vs rdkafka.so — пов'язаний із специфікою нашого проекту. У нас PHP, а в PHP, як правило, всі бібліотеки спілкуються з Kafka через репозиторій rdkafka.so, а далі йде якась обгортка. Можливо, це наші особисті труднощі, але виявилося, що просто перечитати шматочок вже прочитаного не так просто. Загалом були програмні проблеми.

Повертаючись до особливостей роботи з partitions, прямо в документації написано consumers >= topic partitions. Але я дізнався про це набагато пізніше, ніж хотілося б. Якщо ви хочете масштабуватись і мати два консьюмери, вам потрібно як мінімум два partitions. Тобто, якщо у вас був один partition, в якому накопичилося 20 тисяч повідомлень, і ви зробили свіжий, кількість повідомлень вирівняється порівну нескоро. Тому, щоб мати два паралельні консьюмери, треба розбиратися з partitions.

моніторинг

Думаю, через те, як ми моніторимо, буде ще зрозуміліше, які проблеми є в існуючому підході.

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

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Крім того, треба моніторити, як справи у прод'юсера, чи прийняв events-bus повідомлення, і як справи у консьюмера. Наприклад, на графіках нижче у Refund Tool все добре, а у BOB явно якісь проблеми (сині піки).

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Я вже згадував consumer-group lag. Грубо кажучи, це кількість непрочитаних повідомлень. Загалом консьюмери у нас працюють швидко, тому лаг зазвичай дорівнює 0, але іноді може бути короткочасний пік. Kafka вміє це з коробки, але потрібно задати якийсь інтервал.

Є проект Нораякий дасть вам більше інформації по Kafka. Він просто по API consumer-group віддає статус, як у цієї групи справи. Крім ОК і Failed, там є warning, і ви зможете дізнатися, що ваші консьюмери не справляються з темпом прод'юсингу - не встигають вичитувати те, що пишеться. Система досить розумна, її зручно використати.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

Так виглядає відповідь API. Тут група bob-live-fifa, partition refund.update.v1, статус ОК, lag 0 - останній кінцевий offset такий-то.

Досвід розробки сервісу Refund Tool з асинхронним API на Kafka

моніторинг updated_at SLA (stuck) я вже згадував. Наприклад, товар перейшов у статус, що готовий до повернення. Ми ставимо Cron, який каже, що якщо за 5 хвилин цей об'єкт не перейшов у refund (ми повертаємо гроші через платіжні системи дуже швидко), то щось точно пішло не так, і це випадок для саппорта. Тому просто беремо Cron, який читає такі штуки і якщо вони більше 0, то надсилає алерт.

Підсумовуючи, використовувати події зручно, коли:

  • інформація потрібна кільком системам;
  • важливий результат обробки;
  • подій небагато чи події маленькі.

Здавалося б, у статті цілком конкретна тема – асинхронний API на Kafka, але у зв'язку з нею хочеться відразу багато чого порекомендувати.
По-перше, наступний HighLoad ++ потрібно чекати до листопада, вже в квітні буде його пітерська версія, а в червні поговоримо про високі навантаження в Новосибірську.
По-друге, автор доповіді Сергій Заїка входить до Програмного комітету нашої нової конференції з управління знаннями KnowledgeConf. Конференція одноденна пройде 26 квітня, але програма в неї дуже насичена.
А ще у травні буде PHP Ukrainian и РІТ++ (з DevOpsConf у складі) - туди ще можна запропонувати свою тему, розповісти про свій досвід і поскаржитися на свої шишки.

Джерело: habr.com

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