PHP-демон в контейнере Kubernetes: как сделать асинхронную доставку сообщений
Рассказал, как с использованием PHP-демона в Kubernetes сделать так, чтобы сообщения доставлялись пользователям асинхронно и без лагов.
451 открытий3К показов
Представьте, что вы владелец хостинга, где пользователи иногда создают бэкапы. Должен ли провайдер уведомить пользователя, когда все будет готово? Конечно. А сообщить, если в процессе что-то пойдет не так, и пользователю нужно будет прийти и принять решение? Тем более.
Задача веб-разработчика — сделать так, чтобы сообщения доставлялись пользователям с минимальной нагрузкой на систему и без временных лагов.
Виталий Киреев
Руководитель отдела исследований и разработок хостинг-провайдера SpaceWeb
Меня зовут Виталий Киреев и я руководитель отдела исследований и разработок хостинг-провайдера SpaceWeb. Расскажу, как это реализовать.
Как выглядит бизнес-процесс
Вернемся к примеру из начала. Давайте поясню бизнес-процесс:
- В админ-панель хостинга входит пользователь.
- Пользователь запускает какой-либо процесс (например, создание резервной копии файлов).
- Система запускает скрипт, отслеживающий событие: успешное или неуспешное завершение процесса.
- В случае наступления события, система присылает пользователю уведомление.
Само собой, не все пользователи хотят сидеть и ждать, пока процесс не завершится. Им удобнее получить уведомление — например, пуш или СМС. Обычно для решения подобных задач используют cron. Вероятно, вы знаете принцип работы: вы создаете «расписание», по которому будет запускаться определенный серверный скрипт. В нашем случае он будет проверять, не закончился ли процесс создания бэкапа. Формально, такая технология будет рабочей, но с двумя существенными нюансами.
- Первый недостаток — большую часть времени скрипты будут работать вхолостую. Расписание нельзя подстроить оптимально: пользователи будут создавать задачи неравномерно в течение суток. А значит, нам нужно запускать крон непрерывно —это лишняя нагрузка на сервер.
- Второй недостаток — временной лаг. С момента, когда скрипт уже запущен, до момента, когда он закончил работу и отключился, может проходить до 59 секунд — это обусловлено техническими особенностями технологии cron. Вроде и не много, но иногда пользователю неудобно ждать столько времени, особенно когда у тебя за минуту создается сам бэкап.
Как оптимизировать процесс?
Асинхронность и очереди
Для решения подобной задачи хорошо работают брокеры — программы, которые работают с очередями. Как строится работа таких программ:
- Есть очередь — буфер в памяти, не ограниченный по количеству хранимых сообщений. Очередь пополняется и опустошается.
- Паблишер (или продюсер) — сторона, которая добавляет сообщения в очередь. Паблишеров может быть несколько.
- Подписчики (или консьюмеры) —сторона, которая берет сообщения из очереди и выполняет с ним какое-либо действие. Например, передает в СМС-шлюз — и так пользователь получает свое уведомление. Подписчиков тоже может быть несколько.
Особенность программ-брокеров в том, что в момент, когда подписчик принимает сообщение из очереди, это сообщение удаляется из буфера. Тем самым освобождает место в памяти.Также исключаются ситуации, когда одно сообщение может забрать сразу несколько подписчиков — такое вполне может произойти, если вместо очередей использовать обычные базы данных.
Другое преимущество работы через брокера перед прямой работой с БД в данном кейсе — это скорость транзакций.
RabbitMQ или Kafka?
Стоит чуть подробнее рассказать про выбор технологий. Вообще брокеров десятки, но чаще всего применяют эти два из заголовка.
Лично мы в своем продукте используем RabbitMQ — отчасти потому, что наша инфраструктура уже содержала в себе необходимые библиотеки для работы с этим брокером. Но главный критерий — это способ доставки сообщений. В Rabbit это push-реквест, а в Кафке — pull-реквест.
В нашем случае было важно гарантированно доставить сообщение пользователю, который выполняет пассивную роль. Еще один критерий, почему мы выбрали Rabbit — его PHP-библиотека поддерживает обработку управляющих сигналов Unix, мы используем их для мониторинга работы процесса.
Если дальше сравнивать технологии, у Kafka есть еще один недостаток: журнал хранения сообщений с определенным сроком жизни — нам такое не подходило. У нас пользователь может зайти в панель хоть через полгода — он все равно должен получить все уведомления.
PHP-демон в контейнере Kubernetes
Важная часть работы системы доставки сообщений — это механизм контроля ее работоспособности. Если упал сервис, который занимался постановкой сообщений в очередь и их распределением, связанные микросервисы не смогут обмениваться с ним данными. Соответственно, вся система рухнет, пользователи не получат уведомлений.
Как такая проблема решается «по классике»? Обычно инструментами crone или systemd. Но у них есть недостаток: если с самим брокером все в порядке, вы не узнаете о неполадках. А вместе с тем, если есть проблемы с соединением, например, с СМС-шлюзом, бизнес-логика выполняться не будет. В итоге процесс хоть и формально живой, а сообщения не ходят.
Поэтому мы сейчас думаем внедрить другое решение. Сегодня существует универсальный стандарт проверки, liveness probe. Скрипт проверяет соединение с базой, СМС-шлюзом, шлюзом push-уведомлений — то есть мониторит состояние всего процесса целиком.
PHP-демон в таком случае упаковывается в контейнер оркестратора, а он в свою очередь перезапускает демона, если скрипт по итогам проверки выявил проблемы. Утерянные соединения восстанавливаются.
На текущий момент решение содержит вариант проверки Docker Healthcheck — проверка запускается при отправке процессу управляющего сигнала Unix, который перехватывается и обрабатывается в PHP-демоне с помощь библиотеки php-amqplib и позволяет выполнять диагностику работоспособности внутри самого демона:
Сам демон включает интерфейсы, необходимые для взаимодействия с RabbitMQ и с нашим кодом. Он выступает как middleware для всей системы.
Что на выходе
Такое решение отлично заменяет cron и его более современные аналоги. С точки зрения устойчивости системы получаем более безопасный вариант, так как непрерывный мониторинг работоспособности всей системы страхует ее от внезапных падений отдельных микросервисов и нарушения соединений между ними.
Уведомления отправляются без задержек, что было бы неизбежно при использовании перебора по крону: задержки составляли бы от одной до двух минут. Также мы не нагружаем сервер: в состоянии «покоя» демон практически не потребляет ресурсов.
451 открытий3К показов