it-swarm.com.ru

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

Мне нужно запустить PHP-скрипт как процесс демона (ждать инструкций и делать вещи). Работа cron не сделает это для меня, потому что действия должны быть предприняты, как только прибудет инструкция. Я знаю, что PHP на самом деле не лучший вариант для процессов-демонов из-за проблем с управлением памятью, но по разным причинам мне приходится использовать PHP в этом случае. Я наткнулся на инструмент libslack под названием Daemon ( http://libslack.org/daemon ), который, кажется, помогает мне управлять процессами демона, но за последние 5 лет не было никаких обновлений, поэтому мне интересно если вы знаете некоторые другие альтернативы, подходящие для моего случая. Любая информация будет по достоинству оценена.

138
Beier

Вы можете запустить свой php-скрипт из командной строки (например, bash), используя 

Nohup php myscript.php & 

& помещает ваш процесс в фоновый режим.

Правка:
Да, есть некоторые недостатки, но их невозможно контролировать? Это просто неправильно.
Простой kill processid остановит его. И это все еще лучшее и простое решение. 

156
Henrik P. Hessel

Другой вариант заключается в использовании Upstart . Первоначально он был разработан для Ubuntu (и поставляется с ним по умолчанию), но предназначен для всех дистрибутивов Linux.

Этот подход аналогичен Supervisord и 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

Спасибо

Большое спасибо Кевин ван Зонневельд , где я изучил эту технику.

150
Jonathan

Если вы можете - возьмите копию Расширенное программирование в среде UNIX . Вся глава 13 посвящена программированию демонов. Примеры написаны на C, но все нужные вам функции имеют обертки в PHP (в основном расширения pcntl и posix ).

В двух словах - написание демона (это возможно только на ОС * nix - Windows использует сервисы) выглядит так:

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

Кроме того, поскольку вы используете PHP, будьте осторожны с циклическими ссылками, поскольку сборщик мусора PHP, до PHP 5.3, не имел возможности собирать эти ссылки, и процесс будет утекать до тех пор, пока не произойдет сбои.

47
Emil Ivanov

С новым 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 ', но не беспокойтесь, если вы этого не сделаете, это будет предложено, когда это необходимо.

40
LeonanCarvalho

Я запускаю большое количество PHP демонов. 

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

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

Вы можете проверить это в http://cr.yp.to/daemontools.html .

Правка: быстрый список функций.

  • Автоматически запускает демон при перезагрузке
  • Автоматический перезапуск dameon при неудаче
  • Ведение журнала для вас, в том числе опрокидывания и сокращения
  • Интерфейс управления: 'svc' и 'svstat'
  • UNIX дружественный (возможно, не для всех плюс)
24
Phil Wallach

Вы можете

  1. Используйте Nohup, как предложил Хенрик.
  2. Используйте screen и запустите вашу программу PHP как обычный процесс внутри нее. Это дает вам больше контроля, чем использование Nohup
  3. Используйте демон-демон, такой как http://supervisord.org/ (он написан на Python, но может демонизировать любую программу командной строки и дает вам удаленный контроль для управления им). 
  4. Напишите свою собственную оболочку для демонов, как предложил Эмиль, но это лишнее IMO.

Я бы порекомендовал самый простой метод (на мой взгляд, экран), а затем, если вам нужны дополнительные функции или функции, перейдите к более сложным методам. 

14
Noufal Ibrahim

Существует несколько способов решения этой проблемы.

Я не знаю специфики, но, возможно, есть другой способ запустить процесс 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. 

Удачи!

11
rook

Кевин ван Зонневелд написал очень хорошую подробную статью по этому вопросу , в своем примере он использует пакет System_Daemon PEAR (дата последнего выпуска 2009-09-02).

10
Alix Axel

Проверьте https://github.com/shaneharter/PHP-Daemon

Это объектно-ориентированная библиотека демонов. Он имеет встроенную поддержку для таких вещей, как ведение журнала и восстановление после ошибок, а также поддерживает создание фоновых рабочих. 

6
Shane H

Недавно у меня была потребность в кроссплатформенном решении (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 также становился лучше в вопросах утечки памяти. Вы все еще должны быть осторожны (синтаксический анализатор XML DOM имеет тенденцию к утечке), но я редко вижу сбежавшие процессы в наши дни, а средство отслеживания ошибок PHP довольно тихо по сравнению с прошлыми днями.

3
CubicleSoft

Как уже упоминали другие, запустить PHP в качестве демона довольно просто, и это можно сделать с помощью одной строки команды. Но настоящая проблема заключается в том, чтобы поддерживать его в рабочем состоянии и управлять им. У меня была такая же проблема довольно давно, и хотя уже есть множество доступных решений, большинство из них имеют много зависимостей или сложны в использовании и не подходят для базового использования. Я написал сценарий Shell, который может управлять любым процессом/приложением, включая PHP cli-сценарии. Он может быть установлен как cronjob для запуска приложения и будет содержать приложение и управлять им. Если он выполняется снова, например, через тот же cronjob, он проверяет, запущено ли приложение или нет, если он это делает, то просто завершает работу и позволяет своему предыдущему экземпляру продолжать управлять приложением.

Я загрузил его на github, не стесняйтесь использовать его: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Просто следит за вашим приложением (запуск, перезагрузка, запись, мониторинг и т.д.). универсальный сценарий, чтобы убедиться, что ваше приложение продолжает работать правильно. Преднамеренно он использует имя процесса instread файла pid/lock, чтобы предотвратить все его побочные эффекты и сделать скрипт как можно более простым и максимально быстрым, поэтому он всегда работает, даже когда перезапускается сам EasyDaemonizer.

  • Запускает приложение и дополнительно настраивает задержку для каждого запуска
  • Уверен, что работает только один экземпляр
  • Отслеживает использование процессора и автоматически перезапускает приложение, когда оно достигает определенного порога
  • Настройка EasyDeamonizer для запуска через cron, чтобы запустить его снова, если он остановлен по какой-либо причине
  • Журналы своей деятельности
1
Sina Salek

Расширение Эмиль Иваов ответ, Вы можете сделать следующее, чтобы закрыть 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 вне Интернета

1
Raheel Khan

Я написал и развернул простой php-демон, код здесь

https://github.com/jmullee/PhpUnixDaemon

Особенности: удаление привилегий, обработка сигналов, регистрация

Я использовал его в обработчике очереди (сценарий использования: вызвать длительную операцию с веб-страницы, не заставляя генерирующий страницу php ждать, т.е. запустить асинхронную операцию) https://github.com/jmullee/PhpIPCMessageQueue

0
jmullee

вы можете проверить PM2 здесь, http://pm2.keymetrics.io/

создайте ssh-файл, такой как worker.sh, поместите в свой php-скрипт, с которым вы будете иметь дело.

worker.sh

php /path/myscript.php

запуск демона

pm2 start worker.sh

Ура, вот и все.

0
Serkan Koch