Запуск php скрипта как демона

Как запустить php-скрипт как процесс-демон

Мне нужно запустить 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 myphpdaemon

Сценарий PHP должен иметь своего рода «цикл» для продолжения работы.

gc_enable();//

while (!connection_aborted() || PHP_SAPI == «cli»)

//sleep и usleep могут быть полезны

if (PHP_SAPI == «cli»)

if (rand(5, 100) % 5 == 0)

gc_collect_cycles();//Выполняет сбор всех существующих циклов

Читайте также:  Config file for python script

>

>

>

Рабочий пример:

[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»$».php» fixedparameter $ > /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

  1. Вызовите umask(0) для предотвращения проблем с разрешениями.
  2. Вызовите fork() и заставьте родительский процесс завершиться.
  3. Вызовите setid().
  4. Настройте обработку сигналов SIGHUP (обычно этот сигнал игнорируется или используется для сигнализации демону о необходимости перезагрузки его конфигурации) и SIGTERM (чтобы сообщить процессу о завершении).
  5. Снова выполните fork() и попросите родительский процесс завершиться.
  6. Измените текущий рабочий каталог с помощью chdir().
  7. fclose() stdin, stdout и stderr , и не пишите в них. Правильный способ — перенаправить их либо в /dev/null, либо в файл, но я не смог найти способ сделать это в PHP. Возможно, при запуске демона их можно перенаправить с помощью оболочки.

Ответ 5

  1. Автоматический запуск демона при перезагрузке .
  2. Автоматический перезапуск демона при сбое .
  3. Ведение журнала, включая перенос и обрезку.
  4. Интерфейс управления: ‘svc’ и ‘svstat’ .
  5. Дружественный к UNIX (возможно, не для всех это плюс) .

Ответ 6

Существует несколько способов решения этой проблемы. Я не знаю вашей специфики, но, возможно, есть другой способ запустить процесс PHP. Например, если вам нужно, чтобы код выполнялся на основе событий в базе данных SQL, вы можете настроить триггер для выполнения вашего скрипта. Это очень просто сделать в PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html.

Я думаю, что лучше всего создать процесс D ae mon с помощью 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-скрипта.

Читайте также:  Css стиль внутри элемента

Ответ 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 все еще склонен к утечкам).

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Источник

Демоны на PHP

Прежде, чем начать: я знаю, что такое phpDaemon и System_Daemon. Я читал статьи по этой тематике, и на хабре тоже.

  • Запускаться из консоли и отвязываться от неё
  • Всю информацию писать в логи, ничего не выводить в консоль
  • Уметь плодить дочерние процессы и контролировать их
  • Выполнять поставленную задачу
  • Корректно завершать работу

Отвязываемся от консоли

// Создаем дочерний процесс // весь код после pcntl_fork() будет выполняться двумя процессами: родительским и дочерним $child_pid = pcntl_fork(); if ($child_pid) < // Выходим из родительского, привязанного к консоли, процесса exit(); >// Делаем основным процессом дочерний. posix_setsid(); // Дальнейший код выполнится только дочерним процессом, который уже отвязан от консоли 

Функция pcntl_fork() создает дочерний процесс и возвращает его идентификатор. Однако переменная $child_pid в дочерний процесс не попадает (точнее она будет равна 0), соответственно проверку пройдет только родительский процесс. Он завершится, а дочерний процесс продолжит выполнение кода.

В общем то демона мы уже создали, однако всю информацию (включая ошибки) он всё еще будет выводить в консоль. Да и завершится сразу после выполнения.

Переопределяем вывод

$baseDir = dirname(__FILE__); ini_set('error_log',$baseDir.'/error.log'); fclose(STDIN); fclose(STDOUT); fclose(STDERR); $STDIN = fopen('/dev/null', 'r'); $STDOUT = fopen($baseDir.'/application.log', 'ab'); $STDERR = fopen($baseDir.'/daemon.log', 'ab'); 

Здесь мы закрываем стандартные потоки вывода и направляем их в файл. STDIN на всякий случай открываем на чтение из /dev/null, т.к. наш демон не будет читать из консоли — он от неё отвязан. Теперь весь вывод нашего демона будет логироваться в файлах.

Читайте также:  Python decorator all methods

Поехали!

include 'DaemonClass.php'; $daemon = new DaemonClass(); $daemon->run(); 

После того, как мы переопределили вывод, можно выполнять поставленную демону задачу. Создадим DaemonClass.php и начнем писать класс, который будет делать основную работу нашего демона.

DaemonClass.php

// Без этой директивы PHP не будет перехватывать сигналы declare(ticks=1); class DaemonClass < // Максимальное количество дочерних процессов public $maxProcesses = 5; // Когда установится в TRUE, демон завершит работу protected $stop_server = FALSE; // Здесь будем хранить запущенные дочерние процессы protected $currentJobs = array(); public function __construct() < echo "Сonstructed daemon controller".PHP_EOL; // Ждем сигналы SIGTERM и SIGCHLD pcntl_signal(SIGTERM, array($this, "childSignalHandler")); pcntl_signal(SIGCHLD, array($this, "childSignalHandler")); >public function run() < echo "Running daemon controller".PHP_EOL; // Пока $stop_server не установится в TRUE, гоняем бесконечный цикл while (!$this->stop_server) < // Если уже запущено максимальное количество дочерних процессов, ждем их завершения while(count($this->currentJobs) >= $this->maxProcesses) < echo "Maximum children allowed, waiting. ".PHP_EOL; sleep(1); >$this->launchJob(); > > > 

Мы ожидаем сигналы SIGTERM (завершения работы) и SIGCHLD (от дочерних процессов). Запускаем бесконечный цикл, чтобы демон не завершился. Проверяем, можно ли создать еще дочерний процесс и ждем, если нельзя.

 protected function launchJob() < // Создаем дочерний процесс // весь код после pcntl_fork() будет выполняться // двумя процессами: родительским и дочерним $pid = pcntl_fork(); if ($pid == -1) < // Не удалось создать дочерний процесс error_log('Could not launch new job, exiting'); return FALSE; >elseif ($pid) < // Этот код выполнится родительским процессом $this->currentJobs[$pid] = TRUE; > else < // А этот код выполнится дочерним процессом echo "Процесс с ID ".getmypid().PHP_EOL; exit(); >return TRUE; > 

pcntl_fork() возвращает -1 в случае возникновения ошибки, $pid будет доступна в родительском процессе, в дочернем этой переменной не будет (точнее она будет равна 0).

 public function childSignalHandler($signo, $pid = null, $status = null) < switch($signo) < case SIGTERM: // При получении сигнала завершения работы устанавливаем флаг $this->stop_server = true; break; case SIGCHLD: // При получении сигнала от дочернего процесса if (!$pid) < $pid = pcntl_waitpid(-1, $status, WNOHANG); >// Пока есть завершенные дочерние процессы while ($pid > 0) < if ($pid && isset($this->currentJobs[$pid])) < // Удаляем дочерние процессы из списка unset($this->currentJobs[$pid]); > $pid = pcntl_waitpid(-1, $status, WNOHANG); > break; default: // все остальные сигналы > > 

SIGTERM — сигнал корректного завершения работы. SIGCHLD — сигнал завершения работы дочернего процесса. При завершении дочернего процесса мы удаляем его из списка запущенных процессов. При получении SIGTERM, выставляем флаг — наш «бесконечный цикл» завершится, когда выполнится текущая задача.

Осталось запретить запуск нескольких копий демона, об это отлично написано в этой статье.

UPD: хабраюзер Dlussky в своем комментарии подсказал, что в PHP >= 5.3.0 вместо declare(ticks = 1) надо бы использовать pcntl_signal_dispatch()

Источник

Оцените статью