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


154

Мені потрібно запустити скрипт php як демон-процес (зачекайте інструкцій і виконайте завдання). Cron робота не зробить це для мене, оскільки дії потрібно вжити, як тільки надійде інструкція. Я знаю, що PHP насправді не найкращий варіант для демонстраційних процесів через проблеми з управлінням пам'яттю, але через різні причини мені доводиться використовувати PHP в цьому випадку. Я натрапив на інструмент Libslack під назвою Daemon ( http://libslack.org/daemon ), здається, він допомагає мені керувати процесами демон, але останніх років не було оновлень, тому мені цікаво, чи знаєте ви деякі інші альтернативи, що підходять для мого випадку. Будь-яка інформація буде дуже вдячна.


2
будь ласка, перевір мою відповідь. дякую
Генрік П. Гессель


1
Я наткнувся на цю посаду gonzalo123.com/2010/05/23/…, яку я вірю і розслабляючою, і стабільною.
Тесон

Це зробити дуже просто з systemd
LeonanCarvalho

Відповіді:


167

Ви можете запустити свій скрипт php з командного рядка (тобто bash), використовуючи

nohup php myscript.php &

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

Редагувати:
Так, є деякі недоліки, але неможливо контролювати? Це просто неправильно.
Простий kill processidзупинить це. І це все-таки найкраще і найпростіше рішення.


Якщо термінал існує, процес НЕ вийде. Тому команда "nohup" є. Я використовую сценарій PHP як демон на всіх серверах, як тільки це вже багато років. Можливо, буде краще рішення, але це найшвидше.
CDR

27
Це не перезапустить демон, якщо він не вдасться, і не існує простого способу управління демоном взагалі.
Філ Уолах

6
Я погоджуюся з тим, що тут було сказано - це погане рішення. Слід створити сценарій init з кількох причин: 1) Сценарій Init запускається автоматично при запуску 2) Ви можете керувати демоном за допомогою команд start / stop / restart. Ось приклад з servefault: serverfault.com/questions/229759/…
Simian

1
Гей , хлопці ... мені здається , nohupі &робить те ж саме , : від'єднання запущеного процесу з поточним входу в інстанси оболонки. Навіщо мені вони обоє? Чи можу я не просто робити php myscript.php &чи nohup myscript.php?? Спасибі
nourdine

1
Якщо сценарій пише в stdout (через echo або var_dump), ви можете зафіксувати цю інформацію за допомогою файлу журналу на зразок цього:nohup php myscript.php > myscript.log &
Mischa

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не є послугами, що не працюють на старті. @ matt-sich, схоже, виявив правильний синтаксис. Іншим варіантом є використання Gearman або Resque, що дозволяє обробляти фон та деамонізацію.
ckm

3
Сам Ubuntu переходить до використання systemd замість upstart: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai

72

З новою системою ви можете створити послугу.

Ви повинні створити файл або символьне посилання в /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 для виклику в системний файл служби замість 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 (дочірнє) будуть вбиті.

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 були демонстрацією того, як використовувати сервісні залежності, це не потрібно.
LeonanCarvalho

3
Це дійсно повинно замінити прийняту відповідь, оскільки зараз це 2019.
спеція

47

Якщо можете - захопіть копію розширеного програмування в середовищі UNIX . Весь розділ 13 присвячений демоновому програмуванню. Приклади є на C, але всі необхідні функції мають обгортки в PHP (в основному pcntl та posix ).

У кількох словах - написання демона (це можливо тільки на * nix на базі ОС - ОС, що використовує Windows) виглядає так:

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

Крім того, оскільки ви використовуєте PHP, будьте уважні до циклічних посилань, оскільки збирач сміття PHP до PHP 5.3 не має можливості збирати ці посилання, і процес просочиться в пам’яті, поки він врешті не вийде з ладу.


1
Дякуємо за інформацію. Схоже, демонстраційна програма Libslack в значній мірі виконує всі підготовчі роботи, як ви згадали. Я думаю, що зараз я буду дотримуватися цього, поки не знайду інших хороших альтернатив.
Бейєр

1
Знайшовши цю публікацію, очікуваний код для копіювання та вставлення у старий шалений додаток, який не вдається закрити stdin тощо, був розчарований. : p
ThiefMaster

1
Чому (5) вилка () знову?
TheFox

Працювали для мене - чудова робота!
Гаутам Шарма

Для майбутніх читачів, які запитують, чому роздвоятись двічі: stackoverflow.com/questions/881388/… - TL; DR: запобігає зомбі.
Гедіпунк

24

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

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

Для цього ми використовуємо демомонтоли. Це розумно, чисто і надійно. Насправді ми використовуємо його для запуску всіх наших демонів.

Ви можете перевірити це на веб- сторінці http://cr.yp.to/daemontools.html .

EDIT: Швидкий список функцій.

  • Автоматично запускає демон при перезавантаженні
  • Автоматично перезапустити 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 .

Чесно кажучи, я вважаю, що найкраще створити процес Деймона за допомогою nohup. nohup дозволяє команді продовжувати виконувати навіть після виходу користувача:

nohup php myscript.php &

Однак існує дуже серйозна проблема. Як ви сказали, менеджер пам'яті PHP є повним сміттям, він був побудований з припущенням, що сценарій виконується лише протягом декількох секунд, а потім існує. Ваш сценарій PHP почне використовувати GIGABYTES пам'яті лише через кілька днів. ВИ ТАКОЖ ДОБРІ створити cron-скрипт, який працює кожні 12 або, можливо, 24 години, що вбиває та повторно породжує ваш php-скрипт так:

killall -3 php
nohup php myscript.php &

Але що робити, якщо сценарій опинився посеред роботи? Ну kill -3 - це переривання, це те саме, що робити ctrl + c на CLI. Ваш скрипт php може перехопити це переривання та вийти вишукано за допомогою бібліотеки pcntl PHP: 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 ("файл", "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 також покращується з проблемою витоку пам’яті з часом. Ви все ще повинні бути обережними (аналізатор XML DOM як і раніше просочується), але я рідко бачу втікаючі процеси в ці дні, і PHP-помилка відслідковує досить тихо порівняно з днями раніше.


1

Як вже згадували інші, запустити PHP як демон досить просто, і це можна зробити за допомогою одного командного рядка. Але актуальною проблемою є утримання її та управління ними. У мене була така ж проблема досить давно, і хоча вже існує маса рішень, більшість з них мають багато залежностей або важкі у використанні і не підходять для базових звичок. Я написав сценарій оболонки, який може керувати будь-яким процесом / додатком, включаючи сценарії PHP cli. Це може бути встановлено як запуск програми для запуску програми і міститиме програму та керує нею. Якщо він виконується ще раз, наприклад, через ту саму cronjob, він перевіряє, чи працює програма чи ні, чи вона просто закривається і нехай попередній екземпляр продовжує керувати програмою.

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

EasyDeamonizer

Просто стежте за вашою програмою (запуск, перезапуск, журнал, монітор тощо). загальний сценарій, щоб переконатися, що ваш пристрій продовжує працювати належним чином. Навмисно він використовує введення ім'я процесу файлу pid / lock для запобігання всіх його побічних ефектів і збереження сценарію максимально простим і максимально швидким, тому він завжди працює навіть при перезапуску самого EasyDaemonizer. Особливості

  • Запускає програму та, можливо, індивідуальну затримку для кожного запуску
  • Переконайтеся, що працює лише один примірник
  • Відстежує використання процесора та автоматично перезавантажує додаток, коли він досягне визначеного порогу
  • Встановлення EasyDeamonizer для запуску через cron, щоб запустити його знову, якщо він зупинився з будь-якої причини
  • Реєструє свою активність

1

Розширюючи відповідь Еміля Іваова , Ви можете зробити наступне, щоб закрити 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-файл, наприклад, робочий.sh, поставити у свій скрипт php, з яким ви будете мати справу.

робітник.ш

php /path/myscript.php

стартовий демон

pm2 start worker.sh

Ура, це все.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.