Web

Как запустить 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 <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 использует сервисы) выглядит следующим образом:

  1. Вызовите umask(0) для предотвращения проблем с разрешениями.

  2. Вызовите fork() и заставьте родительский процесс завершиться.

  3. Вызовите setid().

  4. Настройте обработку сигналов SIGHUP (обычно этот сигнал игнорируется или используется для сигнализации демону о необходимости перезагрузки его конфигурации) и SIGTERM (чтобы сообщить процессу о завершении).

  5. Снова выполните fork() и попросите родительский процесс завершиться.

  6. Измените текущий рабочий каталог с помощью chdir().

  7. fclose() stdin, stdout и stderr, и не пишите в них. Правильный способ перенаправить их либо в /dev/null, либо в файл, но я не смог найти способ сделать это в PHP. Возможно, при запуске демона их можно перенаправить с помощью оболочки.

Также, поскольку вы используете PHP, будьте осторожны с циклическими ссылками, так как сборщик мусора PHP, до PHP 5.3, не имеет возможности собирать эти ссылки, и процесс будет «загружать» память, пока в конце концов не завершится.

 

 

Ответ 5

Я запускаю большое количество PHP-демонов. Я согласен с вами, что PHP не самый лучший язык для этого, но демоны используют общий код с компонентами, обращенными к веб, поэтому в целом это хорошее решение.

Мы используем для этого daemontools. Он умный, чистый и надежный. На самом деле, мы используем его для запуска всех наших демонов.

Вы можете ознакомиться с ним по адресу: http://cr.yp.to/daemontools.html.

Краткий список возможностей:

  1. Автоматический запуск демона при перезагрузке.

  2. Автоматический перезапуск демона при сбое.

  3. Ведение журнала, включая перенос и обрезку.

  4. Интерфейс управления: 'svc' и 'svstat'.

  5. Дружественный к 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

Как управлять браузером без мышки: список горячих клавиш и команд

Web

Как заменить URL-адреса в тексте ссылками HTML

Web

Как проверить, что все скобки правильно открыты/закрыты

Web

Преобразование объекта PHP в ассоциативный массив