Написать пост

PHP-демон в контейнере Kubernetes: как сделать асинхронную доставку сообщений

Аватарка пользователя Виталий Киреев

Рассказал, как с использованием PHP-демона в Kubernetes сделать так, чтобы сообщения доставлялись пользователям асинхронно и без лагов.

Представьте, что вы владелец хостинга, где пользователи иногда создают бэкапы. Должен ли провайдер уведомить пользователя, когда все будет готово? Конечно. А сообщить, если в процессе что-то пойдет не так, и пользователю нужно будет прийти и принять решение? Тем более.

Задача веб-разработчика — сделать так, чтобы сообщения доставлялись пользователям с минимальной нагрузкой на систему и без временных лагов.

Меня зовут Виталий Киреев и я руководитель отдела исследований и разработок хостинг-провайдера SpaceWeb. Расскажу, как это реализовать.

Как выглядит бизнес-процесс

Вернемся к примеру из начала. Давайте поясню бизнес-процесс:

  1. В админ-панель хостинга входит пользователь.
  2. Пользователь запускает какой-либо процесс (например, создание резервной копии файлов).
  3. Система запускает скрипт, отслеживающий событие: успешное или неуспешное завершение процесса.
  4. В случае наступления события, система присылает пользователю уведомление.

Само собой, не все пользователи хотят сидеть и ждать, пока процесс не завершится. Им удобнее получить уведомление — например, пуш или СМС. Обычно для решения подобных задач используют cron. Вероятно, вы знаете принцип работы: вы создаете «расписание», по которому будет запускаться определенный серверный скрипт. В нашем случае он будет проверять, не закончился ли процесс создания бэкапа. Формально, такая технология будет рабочей, но с двумя существенными нюансами.

  1. Первый недостаток — большую часть времени скрипты будут работать вхолостую. Расписание нельзя подстроить оптимально: пользователи будут создавать задачи неравномерно в течение суток. А значит, нам нужно запускать крон непрерывно —это лишняя нагрузка на сервер.
  2. Второй недостаток — временной лаг. С момента, когда скрипт уже запущен, до момента, когда он закончил работу и отключился, может проходить до 59 секунд — это обусловлено техническими особенностями технологии cron. Вроде и не много, но иногда пользователю неудобно ждать столько времени, особенно когда у тебя за минуту создается сам бэкап.

Как оптимизировать процесс?

Асинхронность и очереди

Для решения подобной задачи хорошо работают брокеры — программы, которые работают с очередями. Как строится работа таких программ:

  1. Есть очередь — буфер в памяти, не ограниченный по количеству хранимых сообщений. Очередь пополняется и опустошается.
  2. Паблишер (или продюсер) — сторона, которая добавляет сообщения в очередь. Паблишеров может быть несколько.
  3. Подписчики (или консьюмеры) —сторона, которая берет сообщения из очереди и выполняет с ним какое-либо действие. Например, передает в СМС-шлюз — и так пользователь получает свое уведомление. Подписчиков тоже может быть несколько.

Особенность программ-брокеров в том, что в момент, когда подписчик принимает сообщение из очереди, это сообщение удаляется из буфера. Тем самым освобождает место в памяти.Также исключаются ситуации, когда одно сообщение может забрать сразу несколько подписчиков — такое вполне может произойти, если вместо очередей использовать обычные базы данных.

Другое преимущество работы через брокера перед прямой работой с БД в данном кейсе — это скорость транзакций.

RabbitMQ или Kafka?

Стоит чуть подробнее рассказать про выбор технологий. Вообще брокеров десятки, но чаще всего применяют эти два из заголовка.

Лично мы в своем продукте используем RabbitMQ — отчасти потому, что наша инфраструктура уже содержала в себе необходимые библиотеки для работы с этим брокером. Но главный критерий — это способ доставки сообщений. В Rabbit это push-реквест, а в Кафке — pull-реквест.

В нашем случае было важно гарантированно доставить сообщение пользователю, который выполняет пассивную роль. Еще один критерий, почему мы выбрали Rabbit — его PHP-библиотека поддерживает обработку управляющих сигналов Unix, мы используем их для мониторинга работы процесса.

Если дальше сравнивать технологии, у Kafka есть еще один недостаток: журнал хранения сообщений с определенным сроком жизни — нам такое не подходило. У нас пользователь может зайти в панель хоть через полгода — он все равно должен получить все уведомления.

PHP-демон в контейнере Kubernetes

Важная часть работы системы доставки сообщений — это механизм контроля ее работоспособности. Если упал сервис, который занимался постановкой сообщений в очередь и их распределением, связанные микросервисы не смогут обмениваться с ним данными. Соответственно, вся система рухнет, пользователи не получат уведомлений.

Как такая проблема решается «по классике»? Обычно инструментами crone или systemd. Но у них есть недостаток: если с самим брокером все в порядке, вы не узнаете о неполадках. А вместе с тем, если есть проблемы с соединением, например, с СМС-шлюзом, бизнес-логика выполняться не будет. В итоге процесс хоть и формально живой, а сообщения не ходят.

Поэтому мы сейчас думаем внедрить другое решение. Сегодня существует универсальный стандарт проверки, liveness probe. Скрипт проверяет соединение с базой, СМС-шлюзом, шлюзом push-уведомлений — то есть мониторит состояние всего процесса целиком.

PHP-демон в таком случае упаковывается в контейнер оркестратора, а он в свою очередь перезапускает демона, если скрипт по итогам проверки выявил проблемы. Утерянные соединения восстанавливаются.

На текущий момент решение содержит вариант проверки Docker Healthcheck — проверка запускается при отправке процессу управляющего сигнала Unix, который перехватывается и обрабатывается в PHP-демоне с помощь библиотеки php-amqplib и позволяет выполнять диагностику работоспособности внутри самого демона:

PHP-демон в контейнере Kubernetes: как сделать асинхронную доставку сообщений 1
PHP-демон в контейнере Kubernetes: как сделать асинхронную доставку сообщений 2

Сам демон включает интерфейсы, необходимые для взаимодействия с RabbitMQ и с нашим кодом. Он выступает как middleware для всей системы.

Что на выходе

Такое решение отлично заменяет cron и его более современные аналоги. С точки зрения устойчивости системы получаем более безопасный вариант, так как непрерывный мониторинг работоспособности всей системы страхует ее от внезапных падений отдельных микросервисов и нарушения соединений между ними.

Уведомления отправляются без задержек, что было бы неизбежно при использовании перебора по крону: задержки составляли бы от одной до двух минут. Также мы не нагружаем сервер: в состоянии «покоя» демон практически не потребляет ресурсов.

Следите за новыми постами
Следите за новыми постами по любимым темам
277 открытий1К показов