Мне нужно запустить php-скрипт в качестве процесса-демона (для ожидания инструкций и делать еще что-то). Задание cron не подойдет, потому что действия должны быть предприняты сразу после получения инструкции. Я знаю, что PHP — не самый лучший вариант для процессов-демонов из-за проблем с управлением памятью, но по разным причинам я должен использовать PHP для моего случая. Я наткнулся на инструмент от libslack под названием Daemon (http://libslack.org/daemon), который, кажется, помогает мне управлять процессами демона, но за последние 5 лет не было никаких обновлений, поэтому мне интересно, знаете ли вы другие альтернативы, подходящие для моего случая. Любая информация будет очень полезной.
Ответ 1
Вы можете запустить свой php-скрипт из командной строки (т. е. bash), используя:
nohup php myscript.php &
«&» переводит ваш процесс в фоновый режим.
Ответ 2
Другой вариант — использовать Upstart. Он был изначально разработан для Ubuntu (и поставляется с ним по умолчанию), но предназначен для всех дистрибутивов Linux. Этот подход похож на Supervisord и daemontools, поскольку он автоматически запускает демон при загрузке системы и возобновляет работу по завершении сценария.
Как настроить:
Создайте новый файл сценария в /etc/init/myphpworker.conf. Вот пример:
# Информация
description "My PHP Worker"
author "Jonathan"
# События
start on startup
stop on shutdown
# Автоматический ответ
respawn
respawn limit 20 5
# Запуск скрипта!
# Обратите внимание, что в этом примере, если ваш PHP скрипт возвращает
# строку "ERROR", демон остановится сам.
script
[ $(exec /usr/bin/php -f /path/to/your/script.php) = 'ERROR' ] && ( stop; exit 1; )
end script
Запуск и остановка вашего демона:
sudo service myphpworker start
sudo service myphpworker stop
Проверьте, запущен ли ваш демон:
sudo service myphpworker status
Ответ 3
С новым systemd вы можете создать сервис.
Вы должны создать файл или символическую ссылку на /etc/systemd/system/, например, myphpdaemon.service, и разместить контент, подобный этому, myphpdaemon будет именем службы:
[Unit]
Description=My PHP Daemon Service
#Может быть, вашему скрипту нужен MySQL или другие сервисы для работы, например, MySQL Memcached
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service
[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/myphpdaemon.pid
ExecStart=/usr/bin/php -f /srv/www/myphpdaemon.php arg1 arg2> /dev/null 2>/dev/null
#ExecStop=/bin/kill -HUP $MAINPID
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
StandardOutput=null #Если вы не хотите делать тома логов, вы можете установить его в null, если вы отправили файл или другие опции, он будет отправлять весь вывод php в этот файл.
StandardError=/var/log/myphpdaemon.log
[Install]
WantedBy=default.target
Вы сможете запускать, получать статус, перезапускать и останавливать службы с помощью команды:
systemctl <start|status|restart|stop|enable> myphpdaemon
Сценарий PHP должен иметь своего рода «цикл» для продолжения работы.
<?php
gc_enable();//
while (!connection_aborted() || PHP_SAPI == "cli") {
//sleep и usleep могут быть полезны
if (PHP_SAPI == "cli") {
if (rand(5, 100) % 5 == 0) {
gc_collect_cycles();//Выполняет сбор всех существующих циклов
}
}
}
Рабочий пример:
[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service
[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/php_app_sync.pid
ExecStart=/bin/sh -c '/usr/bin/php -f /var/www/app/private/server/cron/app_sync.php 2>&1 > /var/log/app_sync.log'
KillMode=mixed
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=default.target
Если ваша процедура PHP должна выполняться один раз, вы можете использовать сценарий оболочки или bash, который будет вызываться в служебный файл systemd, а не напрямую в PHP, например:
#!/usr/bin/env bash
script_path="/app/services/"
while [ : ]
do
# очистка
php -f "$script_path"${1}".php" fixedparameter ${2} > /dev/null 2>/dev/null
sleep 1
done
Если вы выбрали эту опцию, вы должны изменить KillMode для процессов mixed. bash (основной) и PHP (дочерний) будут завершены.
ExecStart=/app/phpservice/runner.sh phpfile parameter > /dev/null 2>/dev/null
KillMode=process
Этот метод также эффективен, если вы столкнулись с утечкой памяти.
Ответ 4
Если есть возможность — возьмите книгу «Продвинутое программирование в среде UNIX». Вся глава 13 посвящена программированию демонов. Примеры приведены на C, но все необходимые вам функции имеют обертки в PHP (в основном это расширения pcntl и posix). В нескольких словах, написание демона (это возможно только в ОС на базе *nix — Windows использует сервисы) выглядит следующим образом:
Вызовите umask(0) для предотвращения проблем с разрешениями.
Вызовите fork() и заставьте родительский процесс завершиться.
Вызовите setid().
Настройте обработку сигналов SIGHUP (обычно этот сигнал игнорируется или используется для сигнализации демону о необходимости перезагрузки его конфигурации) и SIGTERM (чтобы сообщить процессу о завершении).
Снова выполните fork() и попросите родительский процесс завершиться.
Измените текущий рабочий каталог с помощью chdir().
fclose() stdin, stdout и stderr, и не пишите в них. Правильный способ — перенаправить их либо в /dev/null, либо в файл, но я не смог найти способ сделать это в PHP. Возможно, при запуске демона их можно перенаправить с помощью оболочки.
Также, поскольку вы используете PHP, будьте осторожны с циклическими ссылками, так как сборщик мусора PHP, до PHP 5.3, не имеет возможности собирать эти ссылки, и процесс будет «загружать» память, пока в конце концов не завершится.
Ответ 5
Я запускаю большое количество PHP-демонов. Я согласен с вами, что PHP — не самый лучший язык для этого, но демоны используют общий код с компонентами, обращенными к веб, поэтому в целом это хорошее решение.
Мы используем для этого daemontools. Он умный, чистый и надежный. На самом деле, мы используем его для запуска всех наших демонов.
Вы можете ознакомиться с ним по адресу: http://cr.yp.to/daemontools.html.
Краткий список возможностей:
Автоматический запуск демона при перезагрузке.
Автоматический перезапуск демона при сбое.
Ведение журнала, включая перенос и обрезку.
Интерфейс управления: 'svc' и 'svstat'.
Дружественный к UNIX (возможно, не для всех это плюс).
Ответ 6
Существует несколько способов решения этой проблемы. Я не знаю вашей специфики, но, возможно, есть другой способ запустить процесс PHP. Например, если вам нужно, чтобы код выполнялся на основе событий в базе данных SQL, вы можете настроить триггер для выполнения вашего скрипта. Это очень просто сделать в PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html.
Я думаю, что лучше всего создать процесс Daemon с помощью nohup. Nohup, который позволяет команде продолжать выполняться даже после того, как пользователь вышел из системы:
nohup php myscript.php &
Однако существует очень серьезная проблема. Менеджер памяти PHP был создан в предположении, что скрипт выполняется всего несколько секунд, а затем прекращает свое существование. Ваш PHP скрипт начнет использовать ГИГАБАЙТЫ памяти уже через несколько дней. Вы ДОЛЖНЫ также создать скрипт cron, который запускается каждые 12 или, может быть, 24 часа, который завершает и снова запускает ваш php-скрипт следующим образом:
killall -3 php
nohup php myscript.php &
Но что, если сценарий находится в процессе работы? Ну, kill -3 — это прерывание, это то же самое, что нажать ctrl+c в CLI. Ваш php-скрипт может поймать это прерывание и выйти из него, используя библиотеку PHP pcntl: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php. Например, так:
function clean_up() {
GLOBAL $lock;
mysql_close();
fclose($lock)
exit();
}
pcntl_signal(SIGINT, 'clean_up');
Идея $lock заключается в том, что PHP-скрипт может открыть файл с помощью fopen("file", "w");. Только один процесс может иметь блокировку записи в файл, поэтому с помощью этого вы можете убедиться, что запущена только одна копия вашего PHP-скрипта.
Ответ 7
Недавно мне понадобилось кроссплатформенное решение (Windows, Mac и Linux) для проблемы запуска PHP-скриптов в качестве демонов. Я решил эту проблему, написав собственное решение на основе C++ и создав двоичные файлы:
https://github.com/cubiclesoft/service-manager/
Полная поддержка Linux (через sysvinit), а также сервисов Windows и Mac OSX launchd.
Если вам нужен только Linux, то пара других решений, представленных здесь, работают достаточно хорошо. В наши дни есть также Upstart и systemd, которые имеют откат к скриптам sysvinit. Но смысл в использовании PHP в том, что он кроссплатформенный по своей природе, поэтому код, написанный на этом языке, имеет довольно хорошие шансы работать везде как есть. Недостатки начинают проявляться, когда в дело вступают некоторые внешние аспекты на уровне ОС, такие как системные службы, но с этой проблемой сталкивается большинство скриптовых языков.
Попытка поймать сигналы в пользовательской среде PHP — не самая лучшая идея. Внимательно прочитайте документацию по pcntl_signal(), и вы быстро узнаете, что PHP обрабатывает сигналы, используя некоторые довольно неприятные методы (в частности 'ticks'), которые поглощают кучу циклов для чего-то, редко встречающегося процессам (т. е. сигналов). Обработка сигналов в PHP также практически недоступна на POSIX-платформах, а поддержка зависит от версии PHP. Изначально это звучит как достойное решение, но до настоящей полезности ему далеко.
Со временем PHP также стал лучше справляться с проблемами утечки памяти. Вы все еще должны быть осторожны (парсер DOM XML все еще склонен к утечкам).
Web