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


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


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


0

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

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

worker.sh

php /path/myscript.php

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

pm2 start worker.sh

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.