Демоны — Основы

Сегодня я расскажу один удивительный факт:

Большинство веб-разработчиков ни разу в своей жизни не писали демонов. И не то что бы им это было не нужно, они просто никогда не задумывались о том что бы написать демона.

Внимание! В статье требуется расширение php pcntl

Введение

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

Для особых служебных задач, веб-разработчики используют скрипты, запускаемые по крону, а для всего остального идеологию: пользователь запросил — сервер выполнил и вернул результат.

Порой, эта цепочка доходит до абсурда, когда пользователю приходится ждать по 2-3 минуты, пока веб-сервер подготовит и вернёт результат.

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

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

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

Пример схемы работы демона:

  1. Пользователь загрузил видео на сайт
  2. Сервер принял видеоролик от пользователя
  3. Вернул пользователю ответ “Видео обрабатываются”
  4. Отправил демону запрос “Обработай видео”
  5. Демон прочитал запрос и начал обрабатывать видео.
  6. Обработав пользовательское видео, демон сообщил “Готово, можно забирать”
  7. Пользователь обновил страницу и увидел своё видео на странице

Хоть этот пример и притянут за уши, он хорошо отображает суть работы демона.

Пользователь отправил запрос — и сразу получил ответ. Теперь он может заняться чем-то другим, пока его запрос обрабатывается. И ответ обработки может для него появится на странице после очередного нажатия 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$