Очередь заданий на PHP/MySQL. Пишу очередной костыль?
В PHP я разбираюсь очень посредственно, но когда-то написал востребованную узким кругом людей систему сбора статистики. Сейчас скрипт, который собирает данные, выполняется от 45 минут и до более часа, и вот — настало время масштабирования во времени.
Хочу написать очередь заданий на PHP, но не заморачиваться с RabbitMQ и прочими MQ, а хранить список заданий в MySQL. В гугле очень мало информации, в основном предлагают готовые решения. Но я ведь хочу сам разобраться, что к чему.
Что я хочу сделать:
Несколько раз в сутки запускается скрипт, который наполняет очередь заданий (урлы).
Раз в 1-3 секунды запускается воркер, который выбирает себе задание (урл) и начинает его обрабатывать (скачивать и класть в БД). Если заданий нет, то отмирает, если есть, то выполняет.
Вопросы:
Как запускать воркер, что делать, чтобы воркеры не превращались в зомбей, и не плодились излишне?
Самый простой способ — по крону запускаете «надсмотрщика» (supervisor). Надсмотрщик сохраняет свой PID в файл, мемкеш или БД и если видит, что другой надсмотрщик уже запущен — молча умирает.
Надсмотрщик должен быть предельно прост, чтобы не падать. Обнаружить падение надсмотрщика можно по тому признаку, что файл с PID есть, а процесса с таким PID нет — значит предыдущий надсмотрщик внезапно завершился. Это можно зафиксировать в лог.
Если надсмотрщик запустился и других надсмотрщиков нет, он берет задания и запускает одного или несколько скриптов-рабочих (worker), каждому раздает по заданию. Если кто-то из рабов успешно завершился, помечаем задачу как выполненную, если умер — он логгирует это и запускает нового, если с N попыток задача так и не сделана, она помечается как невыполнимая и больше не выполняется.
Чтобы повысить время реакции, при отсутсвии задач надсмотрщик может постоянно опрашивать базу, или например слушать UDP порт (через который его можно пнуть и сказать что появилась новая задача).
А в зомби в Линукс процесс превращается только в 1 случае: если его родитель жив, ребенок завершился, а родитель не собрал информацию о завершении (детали в man wait).
Погуглите phpdeamon. Думаю, однопоточного демона тут вполне достаточно. Есть задача — работаем. Нету — слип на 10/60/600 сек. Запуск демона можно будет прикрутить на крон.
Грамотно — не использовать паттерн «костыль», а пользовать готовое решение, разбираясь в нем по ходу дела :)
Готовое опенсурс решение не обазательно будет лучше и содержать более качественный код, чем свое. Пример: вордпресс, Друпал и Джумла — опен сурс, а код страшный как 3-я мировая.
Интересная вещь, но хотелось бы ограничиться самописным решением — код будет наверняка компактным, понятным (мне) и полностью отвечать моим потребностям.
Используйте Gearman. Он предельно прост в использовании и удобен. Ставите расширение для PHP, пишете воркеры ваши и все. Может хранить очереди в MySQL.
Ну я ж говорю, готового не охота использовать. Да и интересней было бы самому написать :)
Но если надоест самому писать, обращусь в сторону Gearman. Слышал, что полезная вещь.