Сегодня я расскажу один удивительный факт:
Большинство веб-разработчиков ни разу в своей жизни не писали демонов. И не то что бы им это было не нужно, они просто никогда не задумывались о том что бы написать демона.
Внимание! В статье требуется расширение php pcntl
Введение
Иногда их даже можно понять. Ведь все мы привыкли считать что прекрасно знаем как работает сайт, знаем наизусть эту идеологию MVC и забивать свою голову чем-то лишним нам совершенно не нужно.
Для особых служебных задач, веб-разработчики используют скрипты, запускаемые по крону, а для всего остального идеологию: пользователь запросил — сервер выполнил и вернул результат.
Порой, эта цепочка доходит до абсурда, когда пользователю приходится ждать по 2-3 минуты, пока веб-сервер подготовит и вернёт результат.
Возможно что это именно тот самый случай, когда нам могут потреботваться демоны.
Википедия определяет демонов как некоторого персонажа, выполняющего задачи, за которые не хотят браться боги. В нашем случае — это программа, которая всегда работает в фоне, и выполняет задачи, которые возлагает на неё программа веб-сервер.
Задачи, выполняемые нашим демоном, могут быть крайне разнообразны. Но в одном они похожи — они выполняются достаточно долго что бы обрабатывать их при отдаче страницы пользователю.
Пример схемы работы демона:
- Пользователь загрузил видео на сайт
- Сервер принял видеоролик от пользователя
- Вернул пользователю ответ “Видео обрабатываются”
- Отправил демону запрос “Обработай видео”
- Демон прочитал запрос и начал обрабатывать видео.
- Обработав пользовательское видео, демон сообщил “Готово, можно забирать”
- Пользователь обновил страницу и увидел своё видео на странице
Хоть этот пример и притянут за уши, он хорошо отображает суть работы демона.
Пользователь отправил запрос — и сразу получил ответ. Теперь он может заняться чем-то другим, пока его запрос обрабатывается. И ответ обработки может для него появится на странице после очередного нажатия F5, либо придти на email или в виде смс на телефон.
Одной из важнейших составляющих демона, является процесс демонизации программы. Это обозначает что в какой-то момент программа отвязывается от консоли и родительской программы её запустившей, что бы случайно закрытый терминал не стал причиной смерти демона.
Перед тем, как начать писать код, нужно освоить несколько терминов:
- PID — Целочисленный идентификатор процесса в Unix системе. Обычно записывается в файл, что бы точно знать что этот процесс уже запущен.
- Fork (форк) — процесс ветвления программы и создание копии процесса уже запущенной программы. Где явно выделяется родительский процесс (который породил другого) и дочерний (который был пораждён)
- Работать мы будем в Unix-системе, и для работы кода необходимо установить (или собрать) модуль pcntl
Пишем простейшего демона
В php процесс демонизации всегда сводится практически к одной команде: pcntl_fork()
// Форкаем процесс
$pid = pcntl_fork();
if ($pid == -1) {
// Ошибка
die('could not fork'.PHP_EOL);
} else if ($pid) {
// Родительский процесс, убиваем
die('die parent process'.PHP_EOL);
} else {
// Отцепляемся от терминала
posix_setsid()
// Новый процесс, запускаем главный цикл
while(true) {
}
}
В этом коде мы делаем форк (копию) процесса, и сохраняем в переменную $pid идентификатор родительского процесса. Так как наша главная задача отвязаться от родителя, который был запущен в терминале, мы завершаем его процесс, а оставшийся (выполняющийся параллельно) - продолжает жить, уже не привязанный ни к чему.
Для того что бы процесс сразу же не завершился, отправляем его в бесконечный цикл.
Наш простейший демон готов. Он умеет демонизироваться и работает постоянно. Можете запустить в консоли этот скрипт и увидеть в списке процессов php.
Теперь проверяем. Запускаем команду ps, видим что у нас висят какие-то свои процессы
ukko@u:/www/daemons$ ps
PID TTY TIME CMD
3865 pts/2 00:00:00 bash
4726 pts/2 00:00:00 ps
ukko@u:/www/daemons$
Сохраняем код в файле daemon.php, запускаем файл, а потом опять команду ps
ukko@u:/www/daemons$ php daemon.php
die parent process
ukko@u:/www/daemons$ ps
PID TTY TIME CMD
3865 pts/2 00:00:00 bash
4764 pts/2 00:00:04 php
4765 pts/2 00:00:00 ps
ukko@u:/www/daemons$
Как тут можно заметить, процесс php остался висеть в памяти, более того он выполняется. Для того что бы убить его, можно набрать команду kill с PID процесса
ukko@u:/www/daemons$ kill 4764
ukko@u:/www/daemons$ ps
PID TTY TIME CMD
3865 pts/2 00:00:00 bash
4813 pts/2 00:00:00 ps
ukko@u:/www/daemons$