Інтерактивна карта клієнтів — Apache Spark Streaming і Яндекс.Карти

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


Все частіше ми чуємо про лямбда-архітектуру. Все частіше хочуть кластеризацію даних в онлайн. Все більше чуємо про використання онлайн машинного навчання (довчання). Караул.


Можна почати рвати на голові волосся, а можна і швидше потрібно методично тренуватися, розвивати розуміння технологій і алгоритмів і на практиці, в умовах «жорсткого бою» і високих навантажень, — відокремлювати корисні і ефективні технологічні рішення від академічного теоретизування.

Сьогодні розповім, як ми зробили інтерактивну карту наших клієнтів з допомогою Apache Spark Streaming і API Яндекс.Карт. Але перш, повторимо архітектурні підходи і швиденько по суті пройдемо по доступним інструментів.

Підходи до обробки масивів даних
Цій проблематиці вже більше 50 років. Суть в тому, що існує грубо 2 принципових підходу до задачі обробки великих масивів інформації — Data Parallelizm і Task Parallelizm.

У першому випадку, однакова ланцюжок обчислень запускається паралельно над перетиналися незмінними частинами вихідних даних. Саме за цим принципом працюють Apache Spark і Hadoop MapReduce.
У другому випадку, все навпаки — над одним фрагментом даних починає виконуватися паралельно кілька ланцюжків обчислень: за цим принципом працюють популярні Apache Spark Streaming, Apache Storm, з деякою натяжкою, Apache Flume.

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


По суті, що Apache Spark Streaming (спасибі UC Berkeley і DataBricks), що Apache Storm (спасибі, Twitter) — реалізують концепцію потокової обробки даних в архітектурі Task Parallel, однак Spark Streaming пішов далі і дозволяє обробити пакет (дискретизированный RDD) також паралельно в дусі Data Parallel. Така особливість дозволяє легко «прикрутити» онлайн кластеризацію пакета — згрупуємо дані в кластера для візуалізації, запросимо дівчат на вечерю… так, про що це я.


Як працює Apache Spark Streaming
Документацію прочитати ви й самі можете, я лише поясню 2-3 словами саму суть. Ненавиджу популізм, умничание і жонглювання незрозумілими термінами — хочеться, щоб знання передавалися простою, доступною мовою і процес передачі приносив задоволення. Ви збираєте дані, що надходять, багато раз в секунду:
і куди душа забажає. Дані, для простоти, упорядковані. Вам потрібно обробити кожен елемент даних:
  • додати хіт до суми хітів за добу
  • зареєструвати координату клієнта по IP-адресою
  • відправити push-повідомлення користувачу про здійснення операції


Spark Streaming збирає елементи даних у впорядкований незмінний RDD за певний фіксований інтервал часу (скажімо, за 10 секунд) і викликає ваш обробник, передаючи RDD на вхід. RDD — це просто колекція зібраних за інтервал даних, не більше.
Якщо за інтервал вдалося зібрати досить великий RDD, вам потрібно постаратися обробити ДО того, як прийде наступний RDD за наступний інтервал. Тому RDD практично обробляти паралельно на декількох серверах кластера. Чим більше потік даних на вході, тим більше серверів додається в потоковий кластер. Сподіваюся, все зрозуміло пояснив.

А якщо все впало? Відвалився шматок кластера, стався null pointer exception у вашому процесорі пакету…


«Кошерні» та «православні» архітектури черг повідомлень
Невелика аркадна вставка. Не так давно, при згадці RabbitMQ або ZeroMQ запановувала тиша і побожний трепет йшов на групу розробників, архітекторів і випадково заблукав верстальника. А бувалі бійці з досвідом виживання в enterprise — згадували Message-oriented middleware і пускали сльозу.

Але, як ми сказали на початку посту, Бигдата напирає. Причому робить це грубо і безцеремонно. Все частіше ми чуємо, що архітектура черг повідомлень, в якій Consumers координуються і мультиплексируются централізовано на сервері(ах) черг, стає «некошерною», т. к. при зростанні навантаження і числа клієнтів їй стає погано (ще б, потрібно тримати всі контексти з лічильниками всіх клієнтів, пробігати по готовим до обробки сокетам шляхом select/pool і займатися іншим садомазахизмом). І «православної» архітектурою все більше вважається реалізована в Apache Kafka, де свою позицію в черзі пам'ятає і зберігає кожен клієнт-consumer, а сервер(и) займається лише видачею повідомлень, за переданим клієнтом ітератора (а точніше — переданим зміщення у файлі, в якому повідомлення і зберігаються на старому, доброму, бородатому жорсткому диску). Звичайно це халтура і перевалювання відповідальності на клієнтів — але… Бигдата — напирає і виявилося, що архитектурка не така вже й безвідповідальна. І навіть Amazon Kinesis взяв її на озброєння. Докладніше про неї корисно. Тільки там тексту багато, наливайте чашку з кавою побільше і з арабікою.


Відновлення після аварії
На чому ми там зупинилися? Все впало… у кого, які дівчата? А, згадав. Так от, коли все впало, consumer, в даному випадку його роль виконує драйвер (їх кілька коробки), який тягне повідомлення з черг, повинен знову передати збережену позицію в черзі і почати читати повідомлення заново. У нашому випадку ми читаємо повідомлення у Spark Streaming з Amazon Kinesis і драйвер регулярно (настроюється), зберігає прочитану з черги позицію в табличку DymanoDB (це доступно з коробки).

Як влаштований наш проект — «Інтерактивна карта клієнтів»

Джерела подій
Під час роботи клієнтів з порталами «Бітрікс24» javascript відправляє в хмару пакет, що описує дію клієнта, IP-адресу і знеособлену інформацію, яка використовується в системі персональних рекомендацій, CRM, бізнес-аналітики та різних моделях машинного навчання всередині компанії.
Приходить в піку щомиті більше 1000 подій. Події збираються в Amazon Kinesis (який, як пам'ятаємо, з «кошерної архітектурою»).

Передача подій в Spark Streaming
Для обробки цих >1000 подій в секунду піднято невеликий кластер Yarn з Spark Streaming (2 машини). Зверніть увагу на обсяг пам'яті, виділений Spark driver. Схоже, можна виділити пам'яті ще менше:


Ще цікавіше наступний скріншот. На ньому видно, що ми встигаємо обробляти >1000 хітів в секунду до того, як прийде наступний пакет за 30 секундний інтервал:


Так, точно, пам'ять драйвер Spark витрачає менше 200МБ, тому прямо зараз ми йому її підріжемо :-):

Загалом видно, що пам'яті використовується досить мало і вся обробка потоку легко поміщається на 2 залізяки, а при бажанні можна і на одному це робити і ніхто не помітить. Круто. Ефективна технологія: >1000 подій в секунду на «дохлом» залозі.

Обробка подій
Тепер найцікавіше. Нам потрібно отримати у кожного хіта клієнта його IP-адресу і… відобразити його домен точкою на Яндекс.Карті, але так, щоб зум по карті працював і нічого не гальмувало!

Для трансляції IP-адрес в координати ми використовуємо одну з популярних бібліотек. Одна проблемка — з коробки об'єкти бібліотечки на java не сериализуются, тому трансляція IP-адрес в координати поки виконується в один потік всередині драйвера Spark. При бажанні, звичайно, можна підняти окремий ресолвер адрес на кожній partition RDD інтервалу — але поки продуктивності вистачає за очі.

Далі у кожного хіта визначаємо домен проекту і зберігаємо в хеш-таблицю пару: домен — координати і час оновлення. Пари старше декількох днів — прибираємо.

Вивантаження даних для Яндекс.Карти
Через певні інтервали часу (настроюється), ми вивантажуємо прив'язку доменів до координат, тобто майбутні точки на карті, в json-файл для подальшого відображення на Яндекс.Карті. На даний момент близько 20к точок.


Растеризатор-кластеризатор для Яндекс.Карт
Довелося згадати javascript :-). Підводний камінь при відображенні точок на Яндекс.Карту виявився один — 20к точок на карті з вбудованою кластеризацией жахливо гальмують браузер клієнта і карта відкривається хвилини. Тому ми скористалися можливістю серверної кластеризації — написали свій простенький растеризатор-кластеризатор, до якого і підключили карту.

Про те, як реалізована сама карта, як ми робимо серверну растеризацію і підводні камені, — я напишу окремий пост, якщо цікаво, скажіть. Загальна архітектура вийшла така:
  • Карта звертається до серверного растеризатору, передаючи координати поточної області
  • Растеризатор зчитує json-файл з парами: домен-координата, кластеризует точки на льоту і віддає результат
  • Карта відображає результати серверної растеризації-кластеризації
Вийшло швидко і просто. Так, не можна зчитувати json-файл, а звертатися до NoSQL… але поки що і так все працює швидко і є таке слово — лінь :-)

Растеризатор написаний на PHP, і на льоту виконувати k-means звичайно самогубство — тому все спрощено і замість кластеризації робиться растеризування. Якщо цікаво, опишу окремим постом.

Підсумки
Ось як виглядає карта активних доменів клієнтів Бітрікс24 (https://www.bitrix24.ru/online-domains-map):


Ось зум:


Вийшла симпатична онлайн-карта доменів Бітрікс24. Зум і серверна кластеризація працюють досить спритно. Порадував Spark Streaming і досить приємний процес створення карти через API Яндекс.Карт. Пишіть, що може бути ще цікаво на цю тему, — ми постараємося докладно розповісти. Всім удачі!

Джерело: Хабрахабр

0 коментарів

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