LSD — это демон, который разработан для замены scribe от Facebook, который сам Facebook забросил и больше не поддерживает. Демон осуществляет доставку событий произвольного формата на заданные сервера. Требований к формату сообщения два: сообщение должно оканчиваться переводом строки и не должно быть больше 1 Мб.
Минимальные требования к замене scribe, которыми мы руководствовались:
- Надежная доставка событий — каждое отправленное событие должно быть доставлено приемникам как минимум 1 раз
- В случае недоступности приемника, события должны буферизоваться на диск
- Работа в качестве роутера — все события идут на набор роутеров, откуда уже доставляются конечным потребителям
- Доставка указанных пользователем категорий на различные группы серверов (например, debug_* идет на logs1.mlan, остальное — на scribeuds1.mlan и scribeuds2.mlan по алгоритму round-robin)
- Доставка событий на конечные сервера в файлы вида "<target_dir>/<category>/<filename>" и поддержание симлинка вида "<category>_current" на файл, в который в данный момент идет запись
- Open-source
- Разделение потока событий с помощью алгоритма round-robin или по хешу от заданного идентификатора (например, шардинг по user_id)
- Устойчивость к падению клиента, сервера, роутеров и приемников — конкретнее, события должны продолжать доставляться, даже если один из роутеров или один из консьюмеров недоступны
- Убедиться, что у вас macOS или Linux
- Установить go с сайта https://golang.org/
- Установить LSD:
go get github.com/badoo/lsd
Конфигурация по умолчанию берется из "conf/lsd.conf". Чтобы указать свой путь до конфига, нужно использовать ключ "-c <config_path>". Поскольку lsd является демоном на нашем «сишном» фреймворке, его секция конфигурации для демона ничем не отличается от остальных демонов. Пример:
"daemon_config": {
"listen": [
{ "proto": "lsd-gpb", "address": "0.0.0.0:3701" },
{ "proto": "lsd-gpb/json", "address": "0.0.0.0:3702" },
{ "proto": "service-stats-gpb", "address": "0.0.0.0:3703" },
{ "proto": "service-stats-gpb/json", "address": "0.0.0.0:3704" },
],
"max_cpus": 0,
"http_pprof_addr": "0.0.0.0:3705",
"pid_file": "/tmp/lsd.pid",
"log_file": "-",
"log_level": "NOTICE", /* для продакшен-использования не рекомендуется использовать уровень DEBUG */
"service_name": "lsd",
"service_instance_name": "<hostname>",
},
Остальные секции будут описаны ниже. В конфигурации должна быть задача либо секция для сервера, либо для клиента, либо обе (роутинг, см. дополнительные настройки для клиента в этом случае):
«Сервером» в LSD называется сторона-приемник, которая пишет принятые события в отдельную директорию. Термин взят по аналогии со scribe. У сервера есть только одна обязательная опция, которая конфигурируется — директория, в которую должны приниматься все события:
"server_config": {
"target_dir": "/local/tmp/lsd-target/",
},
События будут разбиты по категориям, на каждую категорию будет создано по одной директории в соответствии с её названием. Опциональные настройки:
- max_file_size (в байтах) — регулирует максимальный размер получающихся файлов. Соблюдается нестрого, возможно создание файлов большего размера, погрешность может составлять около 1000 строк.
- file_rotate_interval (в секундах) — максимальное количество секунд, которое должно пройти перед созданием нового файла. Соблюдается также нестрого — новый файл создается только при приходе первого события по прошествии указанного интервала. События должны приходить намного чаще, чем file_rotate_interval, чтобы эта опция работала с минимальной погрешностью.
Также можно задать ключ per_category_settings, в котором нужно положить массив объектов, которые содержат те же поля, но ещё с указанием ключа categories, который является списком масок категорий, для которых нужно применить кастомные настройки:
"per_category_settings": [
{
"categories": ["ololo_*", "trololo_*"],
"max_file_size": 1000000,
"file_rotate_interval": 5,
}
],
«Клиентом» в LSD называется сторона-отправитель, которая доставляет события на заданные сервера. Лучше всего формат клиентской части конфига описывает соответствующая секция в proto-файле:
message client_config_t {
message receiver_t {
required string addr = 1;
required uint64 weight = 2;
};
message routing_config_t {
repeated string categories = 1; // category masks list (only "*" is supported as mask)
repeated receiver_t receivers = 2;
optional bool prefix_sharding = 3 [default = false];
}
required string source_dir = 1;
repeated routing_config_t routing = 2;
optional uint64 max_file_size = 3 [default = 1000000]; // max file size for plain files before rotation
required string offsets_db = 4;
optional uint64 usage_check_interval = 5 [default = 60]; // file usage check interval in seconds
optional bool always_flock = 6 [default = false]; // always flock files, required for re-streaming
};
Директория, из которой берутся события для последующей отправки.
Имя файла должно быть в одном из следующих форматов:
- category, category.log, category/anything — запись в такие файлы должна осуществляться атомарно, то есть O_APPEND + блоки размером не более PIPE_BUF (4 Кб в линуксе)
- category_big, category_big.log, category/anything_big, category/anything_big.log — если у файла есть суффикс "_big", то на время чтения из файла берется LOCK_SH, соответственно запись в такие файлы должны идти тоже с O_APPEND + flock(LOCK_EX)
Имя категории получается либо из имени директории, либо из имени файла, если файл находится прямо в source_dir вместо нахождения в собственной поддиректории.
Удалением и ротацией доставленных файлов занимается сам LSD, никаких дополнительных скриптов не требуется. Перед ротацией LSD добавляет к имени файла суффикс ".old", поэтому не нужно самим писать в файлы с таким суффиксом. Если файл все ещё открыт на запись хотя бы одним из процессов, то файл не будет удален и события не потеряются.
- max_file_size (в байтах) — максимальный размер файла перед его ротацией (используется в случае, если запись происходит в файл вида "category.log" или "category_big.log"
- offsets_db — путь до файла со смещениями прочитанных файлов (например, /tmp/lsd-offsets.db). Важно, чтобы этот файл находится не на том же разделе, куда происходит запись файлов с событиями, иначе демон запаникует, когда на соответствующем разделе закончится место — в этом случае паника обязательна, потому что иначе демон не сможет отслеживать, какие файлы прочитаны до каких смещений
- usage_check_interval (в секундах) — интервал проверки использования файлов. Используется при удалении ".old"-файлов. Сканирование происходит путем анализа procfs, поэтому довольно затратно по CPU. Рекомендуется оставить интервал по умолчанию
- always_flock — нужно ли всегда делать flock(...) при чтении из файлов — необходимо выставить в true в случае ре-стриминга («роутеры» в терминах scribe)
В этом поле задается список машрутов — обязательно один маршрут по умолчанию (в котором не указан список категорий), и опционально задаются маршруты для указанных масок категорий. Пример конфигурации, в которой сообщения по умолчанию доставляются на scribeuds1 и scribeuds2, а события с префиксом debug_* — на logs1:
"routing": [
{
"receivers": [
{"addr": "scribeuds1:3701", "weight": 1},
{"addr": "scribeuds2:3701", "weight": 1},
],
},
{
"categories": ["debug_*"],
"receivers": [
{"addr": "logs1:3701", "weight": 1},
],
},
]