Spotify: міграція підсистеми подій в Google Cloud (частина 2)

першої статті ми говорили про те, як працювала стара система доставки повідомлень і ті уроки, які ми винесли з її роботи. У цієї (другої) статті ми розповімо про архітектуру нової системи і про те, чому ми вибрали Google Cloud Pub/Sub в якості транспортного механізму для всіх подій.

image

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

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

Ще одна зміна, яку ми запланували, полягало в тому, щоб у кожного типу події був свій канал, або топік, і події конвертувалися в більш структурований формат на ранніх стадіях процесу. Більше роботи на Продюсерів означає менше часу на конвертування даних у Extract, Transform, Load (ETL) роботі на пізніх етапах. Поділ подій за топикам — це ключова вимога для побудови ефективної системи реального часу.

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

image

Чотири основних компоненти нової системи – Файловий агент (File Tailer), Сервіс доставки подій (Event Delivery Service), Чергу надійної доставки (Reliable Persistent Queue) і ETL сервіс.

У цій конструкції, у Файлового Агента набагато більш вузьке коло обов'язків, ніж у Продюсера в нашій старій системі. Він формує лог-файли з нових подій і пересилає їх в Службу доставки подій. Як тільки він отримує підтвердження, що події отримані, на цьому його робота закінчується. Немає більше складної обробки маркерів кінця файлу або посвідчення, що дані досягли кінцевої точки в HDFS.

Система доставки подій приймає їх від Агента, переводить в кінцевий структурований формат і відправляє в Чергу. Сервіс побудований як RESTful микросервис за допомогою фреймворку Apollo та розгорнуто за допомогою Helios оркестратора, що є загальною схемою для Spotify. Це дозволяє відв'язати клієнтів від визначеної єдиної технології, а також дозволяє перейти на будь-яку іншу базову технологію, не перериваючи обслуговування.

Чергу є ядром системи і в такому вигляді важлива для масштабування відповідно із зростанням потоку даних. Щоб впоратися з простоєм Hadoop, вона повинна надійно зберігати повідомлення протягом декількох днів.

ETL сервіс повинен надійно запобігати дублювання і експортувати дані з Черги в щогодинні складання HDFS. До того, як він відкриє такий пакет нижележащим користувачам, він повинен з високим ступенем ймовірності переконатися, що всі дані для пакета отримані.

У малюнку вище ви можете бачити блок, на якому написано «Сервіс використовує API безпосередньо». Ми вже деякий час відчуваємо, що syslog неідеальний API для Продюсера подій. Коли нова система вступить в дію, а стара повністю зійде зі сцени, буде логічно відмовитися від syslog і почати працювати з бібліотеками, які безпосередньо зможуть спілкуватися з Сервісом доставки подій.

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

Є безліч повідомлень, що Kafka 0.8 успішно використовується у великих компаніях по всьому світу і Kafka 0.8 це значно поліпшена версія порівняно з тією, що використовується у нас зараз. Зокрема, в ній поліпшені брокери Kafka забезпечують надійне постійне сховище. Проект Mirror Maker представив віддзеркалення між дата-центрами, а Camus можна використовувати для експортування структурованих Avro подій в щогодинні складання.

image

Щоб переконатися, що доставка подій може правильно працювати на Kafka 0.8, ми розгорнули тестову систему, яка показана на малюнку вище. Вбудувати простий Kafka Продюсер в Сервіс доставки подій виявилося легко. Щоб переконатися, що система працює коректно від початку і до кінця – від Сервісу доставки подій до HDFS – ми запровадили різноманітні інтеграційні тести наші процеси безперервної інтеграції і доставки.

На жаль, як тільки ця система почала обробляти реальний трафік, вона почала розпадатися на частини. Єдиним компонентом, який виявився стабільним, став Camus (але так як ми не пропускали дуже багато трафіку через систему, ми все ще не знаємо, як Camus повів би себе під навантаженням).

Mirror Maker доставив нам найбільше головного болю. Ми припускали, що він надійно буде віддзеркалювати дані між дата-центрами, але це виявилося просто не так. Він працює тільки в ідеальних умовах (точніше best effort basis). Як тільки в цільовому кластері трапляється проблема, Mirror Maker просто втрачає дані, хоча при цьому і повідомляє вихідного кластера, що дані успішно зеркалированы (зверніть увагу, що це має бути виправлене у Kafka 0.9).

Mirror Maker-и іноді плуталися в тому, хто був лідером в кластері. Лідер іноді забував, що він був лідером, в той час як інші Mirror Maker-и з кластера могли радісно намагатися за ним слідувати. Коли це траплялося, дзеркалювання між дата-центрами зупинялося.

У Kafka Producer також серйозні проблеми зі стабільністю. Якщо один або більше брокерів кластера віддалялися, або навіть просто перезапускались, з певною ймовірністю Продюсер входив у стан, з якого вже не міг вийти сам. В такому стані він не міг проводити жодних подій. Єдиним виходом був повний рестарт сервісу.

Навіть не торкаючись вирішення цих питань, ми зрозуміли, що на приведення системи в робочий стан знадобиться багато сил. Нам необхідно буде визначити стратегію розгортання для Kafka Broker-ів і Mirror Maker-ів, змоделювати необхідні потужності і розпланувати всі системні компоненти, а також задати метрики продуктивності для системи моніторингу Spotify.

Ми опинилися на роздоріжжі. Чи повинні ми зробити значні інвестиції і спробувати змусити Kafka працювати на нас? Чи варто спробувати щось ще?

Google Cloud Pub/Sub
Поки ми боролися з Kafka, інші члени команди Spotify почали експериментувати з Google Cloud. Особливо нас цікавило Cloud Pub/Sub. Здавалося, що Cloud Pub/Sub зможе задовольнити нашу потребу в надійній черзі: він може зберігати недоставлені дані протягом 7 днів, забезпечує надійність за рахунок підтверджень на рівні програми і має «at least-once» семантику доставки.

Крім задоволення наших потреб, у Cloud Pub/Sub є й додаткові переваги:

  1. Доступність – як глобальний сервіс, Pub/Sub доступний у всіх зонах Google Cloud. Передача даних між нашими дата-центрами буде йти не через нашого нормального інтернет провайдера, а буде використовуватися базова мережа Google.
  2. Простий REST API – якби нам не сподобалася клієнтська бібліотека, яку надає Google, то ми легко могли б написати власну.
  3. Операційна відповідальність лежала на ком-то ще – немає потреби створювати модель прорахунку ресурсів або стратегію розгортання, налаштовувати моніторинг та попередження.
Звучить чудово на папері… але занадто добре, щоб бути правдою? Рішення, який ми створили на Apache Kafka, хоча і не були ідеальними, все ж нормально нам служили. У нас було багато досвіду в боротьбі з різними відмовами, доступ до залозу і исходниками, і – теоретично – ми могли знайти джерело будь-якої проблеми. Перехід до керованого сервісу означав, що ми повинні були довірити ведення операцій іншої організації. І при цьому Cloud Pub/Sub рекламувався як бета-версія – ми не знали про який-небудь іншій організації, крім Google, яка використала його в такому масштабі, який був потрібен нам.

Маючи все це на увазі, ми вирішили, що нам потрібен докладний план тестування, щоб бути абсолютно впевненими в тому, що якщо ми перейдемо на Cloud Pub/Sub, то він буде відповідати всім нашим вимогам.

Тестова навантаження Продюсера

Першим пунктом в нашому плані було тестування Cloud Pub/Sub на те, чи зможе він витримати нашу навантаження. В даний час наша робоча навантаження досягає піку 700К подій в секунду. Якщо врахувати майбутнє зростання і можливі сценарії аварійного відновлення, ми зупинилися на тестовій навантаженні 2М подій в секунду. Щоб зовсім добити Pub/Sub, ми вирішили опублікувати весь цей обсяг трафіку в одному дата-центрі, так щоб всі ці запити потрапили у машини Pub/Sub в одній зоні. Ми зробили припущення, що Google розпланував зони як незалежні домени, і що кожна зона може обробляти рівні об'єми трафіку. У теорії, якщо б ми змогли протиснути 2М повідомлень в одну зону, то змогли б передати і количество_зон*2М повідомлень у всіх зонах. Наша надія була на те, що система зможе обробляти цей трафік як на боці виробника, так і на стороні споживача протягом тривалого часу без деградації сервісу.

На самому початку ми натрапили на камінь спотикання: Java клієнт Cloud Pub/Sub працював недостатньо добре. Цей клієнт, як і багато інших Google Cloud API клієнти, автоматично згенерований специфікацій API. Це добре, якщо ви хочете, щоб клієнти підтримували широкий набір мов, але не надто, якщо ви хочете отримати швидкодіючий клієнт для однієї мови.

На щастя, у Pub/Sub є REST API, так що для нас було просто написати власну бібліотеки. Ми створили нового клієнта, думаючи в першу чергу про його швидкодії. Щоб більш ефективно використовувати ресурси, ми використовували асинхронну Java. Ми також додали в клієнт черги і пакетну обробку. (Це не перший раз, коли нам довелося засукати рукави і переписати клієнта Google Cloud API – в іншому проект ми розробили швидкодіючий клієнт для Datastore API.)

C новим клієнтом ми були готові почати навантажувати Pub/Sub по-дорослому. Ми використовували простий генератор для відправки фіктивного трафіку від Сервісу подій до Pub/Sub. Сформований трафік перенаправлявся через два Pub/Sub топіка у співвідношенні 7:3. Щоб згенерувати 2М повідомлень в секунду, ми запустили Сервіс подій на 29 машинах.

image
Кількість успішних запитів в секунду до Pub/Sub з усіх дата-центрів

image
Кількість неуспішних запитів в секунду до Pub/Sub з усіх дата-центрів

image
Вхідний та вихідний мережевий трафік від машин Сервісу подій в bps

Pub/Sub пройшов випробування з честю. Ми опублікували 2М повідомлень без якого-небудь порушення якості і майже не отримали серверних помилок від Pub/Sub бекенду. Включення пакетної обробки і стиснення на машинах Сервісу подій призвело до отримання приблизно в 1 Gpbs трафіку до Pub/Sub.

image
Графік Google Cloud Monitoring для загальної кількості опублікованих повідомлень у Pub/Sub

image
Графік Google Cloud Monitoring для кількості опублікованих повідомлень в топіках Pub/Sub

Корисний побічний ефект нашого тесту – ми змогли порівняти наші внутрішні метрики з метриками, наданими Google. Як показано на Графіках 3 і 6, вони ідеально збігаються.

Тест на стабільність Споживачів

Наш другий важливий тест був присвячений споживання. Протягом 5 днів ми вимірювали end-to-end затримки під великим навантаженням. На час тесту ми публікували, в середньому, близько 800К повідомлень в секунду. Для імітації реальних навантажень, швидкість публікації змінювалася протягом дня. Аби впевнитися, що ми можемо використовувати кілька топіків одночасно, всі дані публікувалися для двох топіків у співвідношенні 7:3.

Злегка здивувало в поведінці Cloud Pub/Sub те, що необхідно створити підписки до збереження повідомлень – поки підписки не існує, жодні дані не зберігаються. Кожна підписка зберігає дані незалежно, і немає обмежень того, скільки підписок може бути у споживача. Споживачі координуються на стороні сервера, і сервер відповідальний за достатнє виділення повідомлень для всіх споживачів, запитують дані. Це дуже відрізняється від Kafka, де дані зберігаються у створеному топіку і кількість споживачів в топіку обмежено кількістю розділів в топіку.

image

У нашому тесті ми створили підписку, годиною пізніше ми почали споживати дані. Ми споживали їх пакетами по 1000 повідомлень. Так як ми не намагалися досягти межі в споживанні, ми хотіли просто злегка перевищити поточний піковий рівень. Це зайняло 8 годин. Після того як ми досягли його, Споживачі продовжили працювати на тому ж рівні, який відповідав швидкості публікації.

Середня end-to-end затримка, яку ми вимірювали в ході тестового періоду – включаючи відновлення backlog – була в районі 20 секунд. Ми не спостерігали втрат повідомлень протягом всього тестового періоду.

Рішення
На цих тестах ми переконалися, що Cloud Pub/Sub це правильний вибір для нас. Затримки були малі і постійні, і єдине обмеження в ємності, з яким ми зіткнулися, була встановлена квота. Коротше кажучи, вибір Cloud Pub/Sub замість Kafka 0.8 для нашої нової платформи доставки повідомлень, був очевидним рішенням.

image

Наступний крок
Після того як події надійно збережені у Pub/Sub, настав час експортувати їх в HDFS. Щоб повною мірою використовувати можливості Google Cloud, ми вирішили спробувати Dataflow.

В останній статті з цієї серії ми розповімо про те, як використовували Dataflow для наших цілей. Залишайтеся з нами!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.