Данные стали бесценным активом, позволяющим компаниям лучше понимать своих пользователей, прогнозировать их поведение и определять тренды. EventNative – проект с открытым исходным кодом, разработанный командой из Jitsu, который позволяет упростить сбор данных о событиях. EventNative поддерживает работу с несколькими хранилищами данных, и ClickHouse – одно из них.
В этой статье мы расскажем как настроить EventNative с ClickHouse, а также в ней приводятся советы по эксплуатации и повышению производительности и надежности.
Загрузка данных в ClickHouse – не такая простая задача, как может показаться на первый взгляд. Обработка потоков с миллионами событий из различных приложений (где у каждого события может быть своя собственная структура) может вызывать значительные затруднения. Ситуация может стать особенно сложной, если в одной продакшн-среде работают разные версии одного и того же приложения (например, разные версии iOS-приложения).
Архитектура EventNative максимально эффективна и надежна. Она состоит из легковесного HTTP-сервера, принимающего поток событий (состоящий из JSON-объектов) и буферизующего его на локальный диск. Отдельный поток приложения занимается обработкой этого буфера, маппингом JSON-объектов с таблицами в ClickHouse, настройкой схемы и хранением данных.
Быстрый запуск ClickHouse и EventNative
В этом разделе мы расскажем об установке узла с ClickHouse и EventNative c помощью официальных Docker-образов.
Обратите внимание, что здесь мы рассматриваем настройку окружения для разработки. В продакшн-сценариях вы можете развернуть несколько узлов EventNative и подключить реплики ClickHouse, чтобы убедиться в доступности данных и масштабировать пропускную способность.
1. Скачиваем Docker-образы
docker pull ksense/eventnative:latest && docker pull yandex/clickhouse-server:latest
2. Запускаем ClickHouse
mkdir ./clickhouse_data && docker run --name clickhouse-test -p 8123:8123 -v $PWD/clickhouse_data:/var/lib/clickhouse yandex/clickhouse-server
3. Настраиваем EventNative
Добавьте следующий текст в ./eventnative.yaml
server: auth: - server_secret: 'ia7i92rqp3mh' # access token. We will need it later for sending events through HTTP API destinations: clickhouse: mode: stream clickhouse: dsns: - "http://default:@host.docker.internal:8123?read_timeout=5m&timeout=5m" db: default data_layout: mappings: fields: - src: /field_1/sub_field_1 action: remove - src: /field_2/sub_field_1 dst: /field_10/sub_field_1 action: move - src: /field_3/sub_field_1/sub_sub_field_1 dst: /field_20 action: move type: DateTime - dst: /constant_field action: constant value: 1000
Также создайте директорию для логов: mkdir ./eventnative-logs
4. Запускаем EventNative
docker run -d -t --name eventnative-test -p 8001:8001 \ -v $PWD/eventnative.yaml:/home/eventnative/app/res/eventnative.yaml \ -v $PWD/eventnative-logs:/home/eventnative/logs/events/ ksense/eventnative:latest
5. Отправляем тестовое событие и проверяем, что оно записалось в ClickHouse
Добавьте следующий объект в ./api.json:
{ "eventn_ctx": { "event_id": "19b9907d-e814-42d8-a16d-c5da51e01531" }, "field_1": { "sub_field_1": "text1", "sub_field_2": 100 }, "field_2": "text2", "field_3": { "sub_field_1": { "sub_sub_field_1": "2020-09-25T12:38:27" } } }
Запустите следующую команду:
curl -X POST -H "Content-Type: application/json" -d @./api.json \ 'http://localhost:8001/api/v1/s2s/event?token=ia7i92rqp3mh'
echo 'SELECT * FROM events;' | curl 'http://localhost:8123/' --data-binary @-
Вы увидите одно событие в базе данных. Тест сработал!
6. Тестируем буферизацию событий
Одна из основных фич EventNative’а — это буфферизация. События записываются во внутреннюю очередь с сохранением на диск. Если база данных (в нашем случае ClickHouse) недоступна, данные не будут потеряны. События будут сохранены локально и попадут в базу данных когда ClickHouse станет доступным опять.
Давайте протестируем эту фичу:
Добавьте следующий JSON в ./api2.json:
{ "eventn_ctx": { "event_id": "4748c7bb-50d4-43a7-91b4-21a5bcccb12e" }, "field_1": { "sub_field_1": "text1", "sub_field_2": 100 }, "field_2": "text2", "field_3": { "sub_field_1": { "sub_sub_field_1": "2020-09-25T12:38:27" } } }
Остановите ClickHouse
docker stop clickhouse-test
Отошлите событие
curl -X POST -H "Content-Type: application/json" -d @./api2.json 'http://localhost:8001/api/v1/s2s/event?token=ia7i92rqp3mh'
Проверьте что ClickHouse действительно не работает
echo 'SELECT * FROM events;' | curl 'http://localhost:8123/' --data-binary @-
Запустите ClickHouse опять
docker start clickhouse-test
Подождите 60 секунд и убедись, что событие не потерялось
echo 'SELECT * FROM events;' | curl 'http://localhost:8123/' --data-binary @-
Управление схемой с помощью EventNative и ClickHouse
EventNative спроектирован так, что вам не потребуется создавать схемы таблиц и поддерживать их. EventNative позаботится об этом автоматически! Каждое поле в JSON-объекте будет сопоставляться с полем в SQL. Если поле отсутствует, оно будет автоматически создано в базе ClickHouse.
Это особенно полезно, если одна команда разработчиков занимается структурой событий, а другая работает с ClickHouse. Приведем пример: фронтенд-разработчик может начать отправлять очень простые данные для отслеживания просмотров страницы продукта (id продукта и его стоимость), а затем будет добавлять более сложные поля (валюту и изображения). Потому что они могут пригодиться.
Пример:
JSON с событием
{ "product_id": "1e48fb70-ef12-4ea9-ab10-fd0b910c49ce", "product_price": 399.99, "price_currency": "USD", "product_type": "supplies", "product_release_start": "2020-09-25T12:38:27", "images": { "main": "picture1", "sub": "picture2" } }
Автоматически сгенерированная структура таблицы:
"product_id" String, "product_price" Float64, "price_currency" String, "product_type" String, "product_release_start" String, "images_main" String, "images_sub" String
По умолчанию, все поля не будут допускать запись Null. Проблема в том, что Nullable-поля влияют на производительность. Но в случае если Null значения необходимы — можно сконфигурировать список полей:
nullable_fields: ['product_id', 'product_price', 'price_currency']
(См полное описание конфигурации mapping’а)
Больше о конфигурация mapping’а
С помощью изменения настроек EventNative может применять к входящим JSON-объектам определенные преобразования, например:
-
Удалять поля
-
Переименовывать поля (включая перенос поля в другой узел)
-
Явно определять тип поля
-
Подставлять константу
Пример
- src: /field_1/sub_field_1 action: remove - src: /field_2/sub_field_1 dst: /field_10/sub_field_1 action: move - src: /field_3/sub_field_1/sub_sub_field_1 dst: /field_20 action: move type: DateTime - dst: /constant_field action: constant value: 1000
В результате применения правил следующий JSON
{ "eventn_ctx": { "event_id": "19b9907d-e814-42d8-a16d-c5da51e01530" }, "field_1": { "sub_field_1": "text1", "sub_field_2": 100 }, "field_2": "text2", "field_3": { "sub_field_1": { "sub_sub_field_1": "2020-09-25T12:38:27" } } }
Превратится в такую запись в базе данных
{ "eventn_ctx_eventn_id": "19b9907d-e814-42d8-a16d-c5da51e01530", "field_1_sub_field_1": "text1", "field_1_sub_field_2": 100, "field_2": "text2", "field_3_sub_field_1_sub_sub_field_1": "2020-09-25T12:38:27.763000Z", "constant_field": 1000 }
Полное описание этой функции вы можете изучить в документации
Советы по повышению производительности
ReplacingMergeTree (или ReplicatedReplacingMergeTree) – лучший движок для данных, созданных EventNative, и вот почему:
-
Обычно данные, созданные EventNative используются в запросах с агрегатными функциями (например, при подсчете количества событий, удовлетворяющих некоторому условию, за заданный период времени). Движки из семейства MergeTree показывают отличную производительность при работе с такими запросами.
-
ReplacingMergeTree (в отличие от MergeTree) имеет приятный побочный эффект – дедупликация данных. Зачастую ошибки в данных обнаруживаются уже после их загрузки. Иногда их необходимо перезаписывать. Поскольку EventNative может хранить логи событий в течении некоторого времени, вы можете написать скрипт для исправления данных и их повторной записи. ReplacingMergeTree позволит избежать дублирования данных (при условии, что у каждого события есть уникальный идентификатор, который используется в качестве ключа)
Если целевая таблица отсутствует, EventNative создаст таблицу с помощью ReplacingMergeTree или ReplicatedReplacingMergeTree (если размер кластера больше 1). Однако, этот механизм можно настроить вручную. Подробнее о создании таблиц можно прочитать в документации
ссылка на оригинал статьи https://habr.com/ru/company/jitsu/blog/538326/
Добавить комментарий