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

154

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

Бейер
источник
2
пожалуйста, проверьте мой ответ. спасибо
Хенрик П. Хессель
1
Я сталкивался с этим сообщением gonzalo123.com/2010/05/23/…, которое, как я верю, одновременно расслабляющее и стабильное.
Teson
Это очень легко сделать с systemd
LeonanCarvalho

Ответы:

167

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

nohup php myscript.php &

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

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

Хенрик П. Хессель
источник
Если терминал существует, процесс НЕ завершится. Вот почему команда "nohup" есть. Я использую PHP-скрипт в качестве демона на всех серверах вот так вот уже много лет. Там может быть лучшее решение, но это самое быстрое.
CDR
27
Это не перезапустит демон, если он выйдет из строя, и не существует простого способа управлять демоном вообще.
Фил Уоллах
6
Я согласен с тем, что здесь было сказано - это плохое решение. Вы должны создать скрипт инициализации по нескольким причинам: 1) скрипт инициализации запускается автоматически при запуске 2) вы можете управлять демоном с помощью команд start / stop / restart. Вот пример из servefault: serverfault.com/questions/229759/…
Simian,
1
Эй, ребята ... мне кажется, nohupи он &делает то же самое: отсоединяет запущенный процесс от текущего экземпляра оболочки. Зачем мне они оба? Могу ли я не просто сделать php myscript.php &или nohup myscript.php?? Спасибо
nourdine
1
Если скрипт пишет в стандартный вывод (через echo или var_dump), вы можете получить эту информацию с помощью файла журнала, например:nohup php myscript.php > myscript.log &
Миша
167

Другой вариант - использовать 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

Спасибо

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

Джонатан
источник
2
любя это. Просто интересно, возможно ли иметь несколько одновременно работающих работников? У меня просто проблема, что одного работника недостаточно.
Мануэль
1
это будет автоматически работать при запуске системы?
Slier
2
Судо "Служба myphpworker start" не работает для меня. Я использовал "sudo start myphpworker", и он отлично работает
Мэтт Сич
3
@Pradeepta Это потому, что в сообщении есть ошибка - я не совсем уверен, что (и не проверял это), но я думаю, что это sudo service myphpworker start/stop/statusработает только со службами, которые /etc/init.dне находятся в службах upstart. @ matt-sich, похоже, раскрыл правильный синтаксис. Другой вариант - использовать Gearman или Resque, что позволяет выполнять фоновую обработку и деамонизацию.
cкм
3
Сама Ubuntu переходит на использование systemd вместо upstart: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai
72

С новым systemd вы можете создать сервис.

Вы должны создать файл или символическую ссылку на /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

This method also is effective if you're facing a memory leak.

Примечание. Каждый раз, когда вы меняете свой «myphpdaemon.service», вы должны запускать «systemctl daemon-reload», но, если вы этого не сделаете, не забудьте, он будет предупрежден, когда это необходимо.

LeonanCarvalho
источник
7
Недооцененный ответ. У тебя есть мой +1.
Гергели Лукачи
2
Потрясающие. Жаль, что мы не могли бы ответить сердцем, потому что это не должно быть похоронено на этой странице.
Джастин
1
Вы должны проверить systemctl status <your_service_name> -lвывод, это даст вам подсказку, что происходит.
LeonanCarvalho
1
@LeandroTupone MySQL и Memcached были демонстрацией того, как использовать служебные зависимости, в которых нет необходимости.
Леонан Карвалью
3
Это должно действительно заменить принятый ответ, поскольку сейчас 2019 год.
spice
47

Если вы можете - возьмите копию Advanced Programming в среде 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, не имел возможности собирать эти ссылки, и процесс будет иметь утечку памяти до тех пор, пока в конечном итоге не произойдет сбой.

Эмиль Иванов
источник
1
Спасибо за информацию. Похоже, что программа-демон libslack в значительной степени выполняет всю подготовительную работу, как вы упомянули. Думаю, пока я буду придерживаться этого, пока не найду другие хорошие альтернативы.
Бейер
1
Нашел этот пост, ожидаемый код для копирования и вставки в дрянное старое приложение, которое не может закрыть стандартный ввод и т. Д., Был разочарован. : p
ThiefMaster
1
Почему (5) fork () снова?
TheFox
Работал для меня - отличная работа!
Гаутам Шарма
Для будущих читателей, спрашивающих, почему нужно разветвляться дважды: stackoverflow.com/questions/881388/… - TL; DR: Предотвращает зомби.
Ghedipunk
24

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

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

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

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

РЕДАКТИРОВАТЬ: быстрый список функций.

  • Автоматически запускает демон при перезагрузке
  • Автоматический перезапуск dameon при неудаче
  • Ведение журнала для вас, в том числе опрокидывания и сокращения
  • Интерфейс управления: 'svc' и 'svstat'
  • UNIX дружественный (возможно, не для всех плюс)
Фил Уоллах
источник
Также устанавливается из репозиториев, например, в apt!
Kzqai
14

Ты можешь

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

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

Нуфал Ибрагим
источник
Не могли бы вы предоставить аналогичную конфигурацию супервизора?
Аликс Аксель
11

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

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

Удачи!

ладья
источник
6

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

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

Шейн Н
источник
3

Недавно у меня возникла потребность в кроссплатформенном решении (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 довольно тихо по сравнению с прошлыми днями.

CubicleSoft
источник
1

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

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

EasyDeamonizer

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

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

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

Рахил
источник
0

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

https://github.com/jmullee/PhpUnixDaemon

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

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

jmullee
источник
0

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

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

worker.sh

php /path/myscript.php

начало демона

pm2 start worker.sh

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

Серкан Кох
источник