Запустите php скрипт как процесс демона
Мне нужно запустить PHP-скрипт как процесс демона (ждать инструкций и делать вещи). Работа cron не сделает это для меня, потому что действия должны быть предприняты, как только прибудет инструкция. Я знаю, что PHP не самый лучший вариант для процессов-демонов из-за проблем с управлением памятью, но по разным причинам мне приходится использовать PHP в этом случае. Я наткнулся на инструмент от libslack под названием Daemon ( http://libslack.org/daemon), который, кажется, помогает мне управлять процессами демона, но за последние 5 лет не было никаких обновлений, поэтому мне интересно, знаете ли вы некоторые другие альтернативы, подходящие для моего случая. Любая информация будет по достоинству оценена.
15 ответов
Вы можете запустить свой php-скрипт из командной строки (т.е. bash), используя
nohup php myscript.php &
&
ставит ваш процесс в фоновом режиме.
Редактировать:
Да, есть некоторые недостатки, но их невозможно контролировать? Это просто неправильно.
Просто kill processid
остановит это. И это все еще лучшее и простое решение.
Другой вариант - использовать Upstart. Первоначально он был разработан для Ubuntu (и поставляется с ним по умолчанию), но предназначен для всех дистрибутивов Linux.
Этот подход аналогичен http://supervisord.org/ и daemontools в том, что он автоматически запускает демона при загрузке системы и вызывает его при завершении скрипта.
Как настроить это:
Создайте новый файл скрипта в /etc/init/myphpworker.conf
, Вот пример:
# Info
description "My PHP Worker"
author "Jonathan"
# Events
start on startup
stop on shutdown
# Automatically respawn
respawn
respawn limit 20 5
# Run the script!
# Note, in this example, if your PHP script returns
# the string "ERROR", the daemon will stop itself.
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
Спасибо
Большое спасибо Кевину ван Зонневельду, где я изучил эту технику.
С новым systemd вы можете создать сервис (на основе Linux на rhel).
Вы должны создать файл или символическую ссылку в /etc/systemd/system/
например myphpdaemon.service и разместите содержимое, подобное этому, myphpdaemon будет названием службы:
[Unit]
Description=My PHP Daemon Service
#May your script needs mysql or other services to run, eg. 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 #It's the default you can change whats happens on stop command
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
StandardOutput=null #If you don't want to make toms of logs you can set it null if you sent a file or some other options it will send all php output to this one.
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") {
//Code Logic
//sleep and usleep could be useful
if (PHP_SAPI == "cli") {
if (rand(5, 100) % 5 == 0) {
gc_collect_cycles(); //Forces collection of any existing garbage 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
# clear
php -f "$script_path"${1}".php" fixedparameter ${2} > /dev/null 2>/dev/null
sleep 1
done
Если вы выбрали эту опцию, вы должны изменить KillMode на mixed
чтобы процессы bash(main) и php(child) были убиты.
ExecStart=/app/phpservice/runner.sh phpfile parameter > /dev/null 2>/dev/null
KillMode=process
Примечание. Каждый раз, когда вы меняете свой "myphpdaemon.service", вы должны запускать "systemctl daemon-reload", но, если вы этого не сделаете, не забывайте, он будет запрашиваться при необходимости.
Если вы можете - возьмите копию Advanced Programming в среде UNIX. Вся глава 13 посвящена программированию демонов. Примеры написаны на C, но все нужные вам функции имеют оболочки в PHP (в основном расширения pcntl и posix).
В двух словах - написание демона (это возможно только на ОС *nix - Windows использует службы) выглядит так:
- Вызов
umask(0)
чтобы предотвратить проблемы с разрешениями. fork()
и есть родительский выход.- Вызов
setsid()
, - Настройка обработки сигнала
SIGHUP
(обычно это игнорируется или используется для подачи сигнала демону для перезагрузки его конфигурации) иSIGTERM
(чтобы сказать процесс, чтобы выйти изящно). fork()
снова и родительский выход.- Изменить текущий рабочий каталог с помощью
chdir()
, fclose()
stdin
,stdout
а такжеstderr
и не пиши им. Правильный способ - перенаправить их на/dev/null
или файл, но я не смог найти способ сделать это в PHP. Когда вы запускаете демон, возможно, перенаправить его с помощью оболочки (вам придется самому выяснить, как это сделать, я не знаю:).- Ты работаешь!
Кроме того, поскольку вы используете PHP, будьте осторожны с циклическими ссылками, так как сборщик мусора PHP, до PHP 5.3, не имел возможности собирать эти ссылки, и процесс будет иметь утечку памяти до тех пор, пока в конечном итоге не произойдет сбой.
Я запускаю большое количество PHP-демонов.
Я согласен с вами, что PHP не лучший (или даже хороший) язык для этого, но демоны делятся кодом с веб-компонентами, так что в целом это хорошее решение для нас.
Мы используем daemontools для этого. Это умный, чистый и надежный. На самом деле мы используем его для запуска всех наших демонов.
Вы можете проверить это на http://cr.yp.to/daemontools.html.
РЕДАКТИРОВАТЬ: быстрый список функций.
- Автоматически запускает демон при перезагрузке
- Автоматический перезапуск dameon при неудаче
- Ведение журнала для вас, в том числе опрокидывания и сокращения
- Интерфейс управления: 'svc' и 'svstat'
- UNIX дружественный (возможно, не для всех плюс)
Вы можете
- использование
nohup
как предложил Хенрик. - использование
screen
и запустить вашу программу PHP как обычный процесс внутри этого. Это дает вам больше контроля, чем при использованииnohup
, - Используйте демон-демон, такой как http://supervisord.org/ (он написан на Python, но может демонизировать любую программу командной строки и предоставить вам удаленный контроль для управления ею).
- Напишите свою собственную оболочку для демонов, как предложил Эмиль, но это лишнее IMO.
Я бы порекомендовал самый простой метод (на мой взгляд, экран), а затем, если вам нужны дополнительные функции или функции, перейдите к более сложным методам.
Существует несколько способов решения этой проблемы.
Я не знаю специфики, но, возможно, есть другой способ запустить процесс PHP. Например, если вам нужен код для запуска на основе событий в базе данных SQL, вы можете настроить триггер для выполнения вашего скрипта. Это действительно легко сделать в PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html.
Честно говоря, я думаю, что вам лучше всего создать процесс Damon с использованием 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-скрипта.
Удачи!
Кевин ван Зонневельд написал очень хорошую подробную статью по этому вопросу, в своем примере он использует System_Daemon
Пакет PEAR (дата последнего выпуска 2009-09-02).
Проверьте https://github.com/shaneharter/PHP-Daemon
Это объектно-ориентированная библиотека демонов. Он имеет встроенную поддержку таких вещей, как ведение журнала и устранение ошибок, а также поддержку создания фоновых рабочих.
Недавно у меня возникла потребность в кроссплатформенном решении (Windows, Mac и Linux) для проблемы запуска PHP-скриптов в качестве демонов. Я решил проблему, написав собственное решение на C++ и создав двоичные файлы:
https://github.com/cubiclesoft/service-manager/
Полная поддержка Linux (через sysvinit), а также запуск служб Windows NT и Mac OSX.
Если вам просто нужен Linux, то пара других решений, представленных здесь, работают достаточно хорошо и, в зависимости от вкуса. В наши дни также есть Upstart и systemd, у которых есть запасные варианты для сценариев sysvinit. Но половина смысла использования PHP в том, что он кроссплатформенный по своей природе, поэтому код, написанный на языке, имеет довольно хорошие шансы работать везде, как есть. Недостатки начинают проявляться, когда в картину входят некоторые внешние нативные аспекты уровня ОС, такие как системные службы, но эта проблема возникает с большинством языков сценариев.
Попытка поймать сигналы, как кто-то здесь предложил в PHP пользователя, не очень хорошая идея. Прочитайте документацию на pcntl_signal()
внимательно, и вы быстро поймете, что PHP обрабатывает сигналы, используя некоторые довольно неприятные методы (в частности, "тики"), которые жуют кучу циклов для чего-то, что процессы редко замечают (например, сигналы). Обработка сигналов в PHP также едва доступна на платформах POSIX, и поддержка отличается в зависимости от версии PHP. Изначально это звучит как приличное решение, но оно не очень полезно.
С течением времени PHP также становился лучше в вопросах утечки памяти. Вы все еще должны быть осторожны (синтаксический анализатор DOM XML имеет тенденцию к утечке), но я редко вижу сбежавшие процессы в наши дни, а средство отслеживания ошибок PHP довольно тихо по сравнению с прошлыми днями.
Вы можете проверить PM2 здесь, http://pm2.keymetrics.io/
создайте ssh-файл, такой как worker.sh, вставьте в свой php-скрипт, с которым вы будете иметь дело.
worker.sh
php /path/myscript.php
начало демона
pm2 start worker.sh
Ура, вот и все.
Я искал простое решение, не требующее установки дополнительных программ и совместимое с распространенными хостингами, которые разрешают доступ по SSH.
Я закончил с этой настройкой для моего чат-сервера:
-rwxr-xr-x 1 crazypoems psacln 309 ene 30 14:01 checkChatServerRunning.sh
-rw-r--r-- 1 crazypoems psacln 3018 ene 30 13:12 class.chathandler.php
-rw-r--r-- 1 crazypoems psacln 29 ene 30 14:05 cron.log
-rw-r--r-- 1 crazypoems psacln 2560 ene 29 08:04 index.php
-rw-r--r-- 1 crazypoems psacln 2672 ene 30 13:29 php-socket.php
-rwxr-xr-x 1 crazypoems psacln 310 ene 30 14:04 restartChatServer.sh
-rwxr-xr-x 1 crazypoems psacln 122 ene 30 13:28 startChatServer.sh
-rwxr-xr-x 1 crazypoems psacln 224 ene 30 13:56 stopChatServer.sh
И скрипты:
startChatServer.sh
#!/bin/bash
nohup /opt/plesk/php/5.6/bin/php -q /var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php > /dev/null &
stopChatServer.sh
#!/bin/bash
PID=`ps -eaf | grep '/var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php' | grep -v grep | awk '{print $2}'`
if [[ "" != "$PID" ]]; then
echo "killing $PID"
kill -9 $PID
else
echo "not running"
fi
перезапуститьChatServer.sh
#!/bin/bash
PID=`ps -eaf | grep '/var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php' | grep -v grep | awk '{print $2}'`
if [[ "" != "$PID" ]]; then
echo "killing $PID"
kill -9 $PID
else
echo "not running"
fi
echo "Starting again"
/var/www/vhosts/crazypoems.org/httpdocs/chat/startChatServer.sh
И последнее, но не менее важное: я поместил этот последний скрипт в задание cron, чтобы проверить, работает ли «сервер чата», и если нет, то запустить его:
Cron работает каждую минуту
-bash-4.1$ crontab -l
* * * * * /var/www/vhosts/crazypoems.org/httpdocs/chat/checkChatServerRunning.sh > /var/www/vhosts/crazypoems.org/httpdocs/chat/cron.log
checkChatServerRunning.sh
#!/bin/bash
PID=`ps -eaf | grep '/var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php' | grep -v grep | awk '{print $2}'`
if [[ "" != "$PID" ]]; then
echo "Chat server running on $PID"
else
echo "Not running, going to start it"
/var/www/vhosts/crazypoems.org/httpdocs/chat/startChatServer.sh
fi
Итак, с этой настройкой:
- Я могу вручную управлять сервисом с помощью скриптов, когда это необходимо (например, техническое обслуживание)
- Задание cron запустит сервер при перезагрузке или при сбое.
Как уже упоминалось, запуск PHP как демона довольно прост, и его можно выполнить с помощью одной строки команды. Но настоящая проблема заключается в том, чтобы поддерживать его в рабочем состоянии и управлять им. У меня была такая же проблема довольно давно, и хотя уже есть множество доступных решений, большинство из них имеют много зависимостей или сложны в использовании и не подходят для базового использования. Я написал сценарий оболочки, который может управлять любым процессом / приложением, включая сценарии PHP Cli. Он может быть установлен как cronjob для запуска приложения и будет содержать приложение и управлять им. Если он выполняется снова, например, через тот же cronjob, он проверяет, запущено ли приложение или нет, если он это делает, то просто завершает работу и позволяет своему предыдущему экземпляру продолжать управлять приложением.
Я загрузил его на github, не стесняйтесь использовать его: https://github.com/sinasalek/EasyDeamonizer
EasyDeamonizer
Просто следит за вашим приложением (запуск, перезагрузка, запись, мониторинг и т. Д.). универсальный скрипт, чтобы убедиться, что ваше приложение продолжает работать правильно. Преднамеренно он использует имя процесса instread файла pid / lock, чтобы предотвратить все его побочные эффекты и сделать скрипт как можно более простым и максимально быстрым, поэтому он всегда работает, даже когда перезапускается сам EasyDaemonizer. Характеристики
- Запускает приложение и дополнительно настраивает задержку для каждого запуска
- Уверен, что работает только один экземпляр
- Отслеживает использование процессора и автоматически перезапускает приложение, когда оно достигает определенного порога
- Настройка EasyDeamonizer для запуска через cron, чтобы запустить его снова, если он остановлен по какой-либо причине
- Журналы своей деятельности
Расширяя ответ Эмиля Иваова, Вы можете сделать следующее, чтобы закрыть STDIN, STDOUT И STDERROR в php
if (!fclose(STDIN)) {
exit("Could not close STDIN");
}
if (!fclose(STDOUT)) {
exit("Could not close STDOUT");
}
if (!fclose(STDERR)) {
exit("Could not close STDERR");
}
$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('/dev/null', 'w');
$STDERR = fopen('/var/log/our_error.log', 'wb');
По сути, вы закрываете стандартные потоки, так что PHP некуда писать. Следующие fopen
вызовы установят стандартный IO в /dev/null
,
Я прочитал это из книги Роба Алея - PHP вне Интернета
Я написал и развернул простой php-демон, код здесь
https://github.com/jmullee/PhpUnixDaemon
Особенности: удаление привилегий, обработка сигналов, регистрация
Я использовал его в обработчике очереди (сценарий использования: вызвать длительную операцию с веб-страницы, не заставляя php генерировать страницу, т.е. запустить асинхронную операцию) https://github.com/jmullee/PhpIPCMessageQueue