Как стать автором
Обновить

Комментарии 34

Удивительно, что не тестировали RabbitMQ.

Мне кажется, что RabbitMQ не удовлетворяет и половине изложенных требований, и вообще является системой более высокого уровня, чем просто доставка событий.

А Apache Kafka удовлетворяла этим требованиям? А на выходе у них получилась не система высокого уровня?

Kafka в целом удовлетворяет требованиям, однако не имеет локального агента и соответственно не может буферизовать события на диск в случае недоступности кластера. Об этом сказано в самом начале.
Почему не было варианта реализовать кастомный продьюсер для кафки с требуемым функционалом?
Да в принципе наверное можно было бы так сделать. На самом деле, код клиента составляет 90% от всего демона, поэтому, чтобы не переписывать консьюмеры для scribe (которые пишут в файлы в описанном в статье формате), можно было уже и сервер написать заодно. Что мы и сделали.
Задача стриминга из файлов (особенно в реальном времени) не такая уж простая из-за того, что сложно реализовать rotate файлов, поэтому её почти никто и не реализует по-человечески. А если писать не в файлы, то могут теряться события при проблемах с демоном. Так что нельзя сказать, что у нас был большой выбор, к сожалению.
Можете рассказать подробнее какие сложности возникают при реализации ротейта по человечески?
Основная проблема заключается в том, чтобы не терять события во время ротейта (переименования и удаления) файлов.
Как реализовать ротацию? Допустим, что кто-то пишет в файл cat.log. В каждый момент времени может быть несколько процессов, которые держат файл открытым, поэтому может вообще не быть «окна» по времени, когда файл можно было бы удалить, чтобы появился новый. Ок, вместо этого переименуем файл в cat.log.old и продолжим стримить этот файл. Скорее всего в скором времени опять появится файл cat.log, потому в приложении записано именно это имя.

Теперь у нас 2 файла: cat.log и cat.log.old. Мы должны стримить оба, поскольку мы не знаем, пишет ли кто-нибудь в старый файл или нет. Переименование в .old-файл происходит по достижении определенного размера, и по умолчанию это по-моему 10 Мб. Допустим, что приложения все еще держат открытым файл cat.log.old, и cat.log уже превысил 10 Мб. Если мы еще раз переименуем cat.log в cat.log.old, то это не вызовет никаких ошибок и старый cat.log.old просто заменится на новый. В итоге мы удалим файл, в который кто-то еще мог писать и события потеряются.

Чтобы этого не происходило, мы должны откладывать ротацию до тех пор, пока .old-файл не перестал использоваться и когда его солержимое полностью доставлено на сервера-приемники. Определить использование можно с использованием утилиты fuser или lsof, но часто вызывать их на каждый файл может быть весьма затратно. Можно также воспользоваться трюком с flock() и заставлять писателей (!) брать LOCK_SH на файл, а необходимым условием для ротации файла тогда будет служить полученный LOCK_EX. К сожалению, эта схема не слишком удобна, особенно если вы хотите дать возможность посылать события откуда угодно, вплоть до shell-скриптов.

Вместо этого, чтобы определить, используется ли файл, LSD периодически (по умолчаниюраз в минуту) сканирует procfs и делает stat() на каждый файловый дескриптор, открытый в системе и сверяет номера inode'ов со списком inode'ов файлов, которые мы хотим заротировать. Это позволяет определить использование всех файлов сразу с небольшим оверхедом. Под капотом fuser и lsof точно также сканируют procfs, поэтому этот способ на самом деле не является чем-то особенным.

Теперь, когда с ротацией разобрались, осталась одна маленькая деталь: мы не хотим делать open()-read()-close() на каждое событие, полученное через inotify, поэтому мы держим открытыми файловые дескрипторы у всех файлов вида category.log. Рано или поздно открытый файловый дескриптор начинает смотреть на файл category.log.old и потом вообще на удаленный файл (после второго переименования). Организовать систему внутренних оповещений об изменении имени файла, оказывается, не так просто, как может показаться на первый взгляд, поскольку каждый файл нужно открыть только один раз и не забывать закрывать файловые дескрипторы для удаленных файлов.
Все ведь сильно упрощается, если в файл пишет ровно один писатель. И если после переименования в cat.log.old появился cat.log, то в cat.log.old уже никто не пишет.
Да, Марко, но дело в том, что мы делали LSD для замены Scribe, а в scribe мы писали из разных процессов php-fpm :).
Спасибо за развернутый ответ.
Мне кажется, основная проблема находится как раз в районе идеи «приложение пишет лог-файлы». Если приложение пишет лог-файлы, это значит, что у вас нет никаких гарантий стабильности системы. Потому что приложение может решить быстро писать логи или писать сообщение о том, что заканчивается место на диске для хранения логов со скоростью быстрее производительности дисковой подсистемы и т.д.

Правильное ршение: перевод всех приложений на syslog или journalctl. Если приложение плохо переводится, то его нужно научить писаться в stdout/stderr, а дальше его сам systemctl сложит в syslog правильным образом.
А почему не использовать уникальное имя для old файлов?
А как это поможет :)? Все равно нужно удалять файлы, когда они полностью доставлены и в них никто не собирается писать.
В чём разница между «событием» и «сообщением»?
Как быть в случае бездисковых серверов? Писать на nfs накладно.
Писать на memory диск?..
В целом, никто не мешает писать в центральный LSD-сервер «напрямую» (через JSON-протокол или GPB-протокол). Но лучше все же писать куда-нибудь на диск, чтобы события могли там лежать какое-то время и не потеряться. Иначе может получиться, что сервер «лежит» и событий накопилось уже много и они не влезают в память и приходится их «дропать».
Я так понял, что там только интеграционные тесты на PHP. А модули покрывали тестами? Рекламирую для этих целей библиотечку матчеров от коллеги: https://github.com/aandryashin/matchers
Нет, модули тестами не покрыты, есть только «самопальные» функциональные тесты на PHP :)
Второй вопрос — не смотрели ли вы на: https://github.com/nsqio/nsq прежде чем писать своё?
Смотрели, это тоже больше похоже на сервер очередей, нежели на транспорт для событий и последующей батч-обработки.
ElasticSearch + Logstash\FileBeat? Мы по крайней мере использовали их. Протобуфы из коробки не переваривает, но JSON родной формат.
Для меня Live Streaming — это что-то из области видеотрансляций. Уже обрадовался — думал, найду тут замену какому-нибудь Wowza Streaming Engine :-)
Спасибо за комментарий. Погуглил — нашел https://github.com/arut/nginx-rtmp-module/wiki/Directives. Вроде, все, что мне надо, есть. Попробую перетащить сайт с Wowza. Реально, спасибо. А то как настроил стриминг в 2014, так и не смотрел, не появилось ли чего нового.

Автор топика, прости за оффтоп :-)
Я бы копал дальше :) В сторону более современного hsl
https://docs.peer5.com/guides/setting-up-hls-live-streaming-server-using-nginx/
У HLS большая задержка. Приятно иметь 4 секунды на rtmp и hls для совместимости. Или я его просто не умею готовить?
НЛО прилетело и опубликовало эту надпись здесь
То, что я намерил, — это настройка Wowza Streaming Engine по умолчанию. При отправке сигнала из Москвы на сервер в Голландии и затем обратно. Плюс какое-то время на перекодирование/пережатие при отправке.

В принципе, я слышал, что можно быстрее, но всё же при работе через дикий интернет иметь небольшой буфер хорошо, как мне кажется. 100 мс — это интересный предел, не знал, что этот протокол так может.
hls работает без flash player, который в скором времени выпилят даже из хрома
Собственно, мы про одно и то же. И Ваша ссылка и ссылка выше ведут на настройку того же модуля.
а как на счет zeromq для транспорта?

Непонятно зачем вышли в опен сорс - выложили исходники и забили на проект.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий