Exécutez le script php en tant que processus démon

154

J'ai besoin d'exécuter un script php en tant que processus démon (attendre les instructions et faire des choses). cron job ne le fera pas pour moi car des actions doivent être prises dès que l'instruction arrive. Je sais que PHP n'est pas vraiment la meilleure option pour les processus démons en raison de problèmes de gestion de la mémoire, mais pour diverses raisons, je dois utiliser PHP dans ce cas. Je suis tombé sur un outil de libslack appelé Daemon ( http://libslack.org/daemon ) il semble m'aider à gérer les processus démon, mais il n'y a pas eu de mises à jour au cours des 5 dernières années, donc je me demande si vous en connaissez d'autres alternatives adaptées à mon cas. Toute information sera vraiment appréciée.

Beier
la source
2
Veuillez vérifier ma réponse. merci
Henrik P. Hessel
1
Je suis tombé sur ce post gonzalo123.com/2010/05/23/ ... qui, je crois, est à la fois relaxant et stable.
Teson
C'est très facile à faire avec systemd
LeonanCarvalho

Réponses:

167

Vous pouvez démarrer votre script php à partir de la ligne de commande (ie bash) en utilisant

nohup php myscript.php &

les & met votre processus en arrière-plan.

Edit:
Oui, il y a quelques inconvénients, mais impossible à contrôler? C'est tout simplement faux.
Un simple kill processidl'arrêtera. Et c'est toujours la solution la meilleure et la plus simple.

Henrik P. Hessel
la source
Si le terminal existe, le processus ne se terminera PAS. C'est pourquoi la commande "nohup" est là. J'utilise un script PHP comme démon sur tous les serveurs comme celui-ci depuis des années maintenant. Il y a peut-être une meilleure solution, mais c'est la plus rapide.
CDR
27
Cela ne redémarrera pas le démon en cas d'échec, et il n'y a aucun moyen facile de gérer le démon du tout.
Phil Wallach
6
Je suis d'accord avec ce qui a été dit ici - c'est une mauvaise solution. Vous devez créer un script d'initialisation pour plusieurs raisons: 1) Le script d'initialisation est lancé automatiquement au démarrage 2) Vous pouvez gérer le démon avec des commandes start / stop / restart. Voici un exemple de servefault: serverfault.com/questions/229759/…
Simian
1
hé les gars ... il me semble nohupet &fait exactement la même chose: détacher le processus lancé de l'intance actuelle du shell. Pourquoi ai-je besoin des deux? Puis-je ne pas simplement faire php myscript.php &ou nohup myscript.php?? Merci
nourdine
1
Si le script écrit sur stdout (via echo ou var_dump), vous pouvez récupérer ces informations avec un fichier journal comme celui-ci:nohup php myscript.php > myscript.log &
Mischa
167

Une autre option consiste à utiliser Upstart . Il a été initialement développé pour Ubuntu (et est livré avec lui par défaut), mais est destiné à convenir à toutes les distributions Linux.

Cette approche est similaire à Supervisord et daemontools , en ce sens qu'elle démarre automatiquement le démon au démarrage du système et réapparaît à la fin du script.

Comment le configurer:

Créez un nouveau fichier de script sur /etc/init/myphpworker.conf. Voici un exemple:

# 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

Démarrage et arrêt de votre démon:

sudo service myphpworker start
sudo service myphpworker stop

Vérifiez si votre démon est en cours d'exécution:

sudo service myphpworker status

Merci

Un grand merci à Kevin van Zonneveld , d'où j'ai appris cette technique.

Jonathan
la source
2
aimer ça. Vous vous demandez simplement, est-il possible d'avoir plusieurs travailleurs simultanés? J'ai juste le problème qu'un seul travailleur ne suffit plus.
Manuel
1
cela fonctionnera-t-il automatiquement au démarrage du système?
slier
2
Sudo "service myphpworker start" ne fonctionnait pas pour moi. J'ai utilisé "sudo start myphpworker" et cela fonctionne parfaitement
Matt Sich
3
@Pradeepta C'est parce qu'il y a une erreur dans le post - je ne sais pas exactement quoi (et je n'ai pas testé cela), mais je pense que cela sudo service myphpworker start/stop/statusne fonctionne qu'avec des services qui ne sont /etc/init.dpas des services de démarrage. @ matt-sich semble avoir découvert la syntaxe correcte. Une autre option consiste à utiliser Gearman ou Resque, qui permet le traitement en arrière-plan et la désamonisation.
ckm
3
Ubuntu lui-même passe à l'utilisation de systemd au lieu de upstart: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai
72

Avec new systemd, vous pouvez créer un service.

Vous devez créer un fichier ou un lien symbolique dans /etc/systemd/system/, par exemple. myphpdaemon.service et placez le contenu comme celui-ci, myphpdaemon sera le nom du service:

[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

Vous pourrez démarrer, obtenir le statut, redémarrer et arrêter les services à l'aide de la commande

systemctl <start|status|restart|stop|enable> myphpdaemon

Le script PHP doit avoir une sorte de "boucle" pour continuer à fonctionner.

<?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
        }
    }
}

Exemple de travail:

[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

Si votre routine PHP doit être exécutée une fois dans un cycle (comme un diggest), vous pouvez utiliser un script shell ou bash pour être appelé dans le fichier de service systemd au lieu de PHP directement, par exemple:

#!/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

Si vous avez choisi ces options, vous devriez changer le KillMode en mixedprocessus, bash (main) et PHP (enfant) être tués.

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.

Remarque: chaque fois que vous modifiez votre "myphpdaemon.service", vous devez exécuter `systemctl daemon-reload ', mais ne vous inquiétez pas si vous ne le faites pas, il sera alerté en cas de besoin.

LéonanCarvalho
la source
7
Réponse sous-estimée. Vous avez mon +1.
Gergely Lukacsy
2
Impressionnant. Je souhaite que nous puissions avoir des réponses cardiaques aussi car cela ne devrait pas être enterré sur cette page.
Justin
1
Vous devriez vérifier la systemctl status <your_service_name> -lsortie, cela vous donnera un indice de ce qui se passe.
LeonanCarvalho
1
@LeandroTupone Le MySQL et Memcached était une démonstration de la façon d'utiliser les dépendances de service, ce n'est pas nécessaire.
LeonanCarvalho
3
Cela devrait vraiment remplacer la réponse acceptée étant donné que nous sommes maintenant en 2019.
Épice
47

Si vous le pouvez, prenez une copie de la programmation avancée dans l'environnement UNIX . L'ensemble du chapitre 13 est consacré à la programmation des démons. Les exemples sont en C, mais toutes les fonctions dont vous avez besoin ont des wrappers en PHP (essentiellement les extensions pcntl et posix ).

En quelques mots - écrire un démon (cela n'est possible que sur les systèmes d'exploitation basés sur * nix - Windows utilise des services) est comme ceci:

  1. Appelez umask(0)pour éviter les problèmes d'autorisation.
  2. fork() et que le parent quitte.
  3. Appel setsid() .
  4. Configuration du traitement du signal de SIGHUP(généralement ignoré ou utilisé pour signaler au démon de recharger sa configuration) et SIGTERM(pour indiquer au processus de se terminer correctement).
  5. fork() à nouveau et que le parent quitte.
  6. Modifiez le répertoire de travail actuel avec chdir().
  7. fclose() stdin, stdoutet stderret ne leur écrivez pas. La bonne façon est de les rediriger vers/dev/null ou l' ou vers un fichier, mais je n'ai pas trouvé de moyen de le faire en PHP. Il est possible lorsque vous lancez le démon de les rediriger à l'aide du shell (vous devrez trouver vous-même comment faire cela, je ne sais pas :).
  8. Fais ton travail!

De plus, puisque vous utilisez PHP, faites attention aux références cycliques, car le garbage collector PHP, avant PHP 5.3, n'a aucun moyen de collecter ces références et le processus perdra de la mémoire, jusqu'à ce qu'il finisse par se planter.

Emil Ivanov
la source
1
Merci pour l'info. Il semble que le programme démon de libslack effectue à peu près tout le travail de préparation comme vous l'avez mentionné. Je pense que pour l'instant je vais m'en tenir à cela jusqu'à ce que je trouve d'autres bonnes alternatives.
Beier
1
J'ai trouvé ce message, le code attendu à copier et coller dans une vieille application merdique qui ne parvient pas à fermer stdin, etc., a été déçu. : p
ThiefMaster
1
Pourquoi (5) fork () encore?
TheFox
A travaillé pour moi - excellent travail!
Gautam Sharma
Pour les futurs lecteurs qui demandent pourquoi bifurquer deux fois: stackoverflow.com/questions/881388/… - TL; DR: Empêche les zombies.
Ghedipunk
24

J'exécute un grand nombre de démons PHP.

Je suis d'accord avec vous que PHP n'est pas le meilleur (ni même un bon) langage pour faire cela, mais les démons partagent le code avec les composants Web, donc dans l'ensemble, c'est une bonne solution pour nous.

Nous utilisons daemontools pour cela. C'est intelligent, propre et fiable. En fait, nous l'utilisons pour exécuter tous nos démons.

Vous pouvez vérifier cela à http://cr.yp.to/daemontools.html .

EDIT: une liste rapide des fonctionnalités.

  • Démarre automatiquement le démon au redémarrage
  • Redémarrez automatiquement dameon en cas d'échec
  • La journalisation est gérée pour vous, y compris le retournement et l'élagage
  • Interface de gestion: 'svc' et 'svstat'
  • Compatible UNIX (pas un plus pour tout le monde peut-être)
Phil Wallach
la source
Également installable à partir des référentiels, par exemple dans apt!
Kzqai
14

Vous pouvez

  1. Utilisez nohupcomme Henrik l'a suggéré.
  2. Utilisez screenet exécutez votre programme PHP comme un processus régulier à l'intérieur. Cela vous donne plus de contrôle que d'utiliser nohup.
  3. Utilisez un démoniseur comme http://supervisord.org/ (il est écrit en Python mais peut démoniser n'importe quel programme en ligne de commande et vous donner un contrôle à distance pour le gérer).
  4. Écrivez votre propre enveloppe de démonisation comme Emil l'a suggéré, mais c'est excessif IMO.

Je recommanderais la méthode la plus simple (écran à mon avis), puis si vous voulez plus de fonctionnalités ou de fonctionnalités, passez à des méthodes plus complexes.

Noufal Ibrahim
la source
Pouvez-vous fournir une configuration de supervision similaire?
Alix Axel
11

Il existe plusieurs manières de résoudre ce problème.

Je ne connais pas les détails mais il existe peut-être un autre moyen de déclencher le processus PHP. Par exemple, si vous avez besoin que le code s'exécute en fonction d'événements dans une base de données SQL, vous pouvez configurer un déclencheur pour exécuter votre script. C'est vraiment facile à faire sous PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html .

Honnêtement, je pense que votre meilleur pari est de créer un processus Damon en utilisant nohup. nohup permet à la commande de continuer à s'exécuter même après la déconnexion de l'utilisateur:

nohup php myscript.php &

Il y a cependant un problème très sérieux. Comme vous l'avez dit, le gestionnaire de mémoire de PHP est une poubelle complète, il a été construit avec l'hypothèse qu'un script ne s'exécute que pendant quelques secondes, puis existe. Votre script PHP commencera à utiliser des GIGABYTES de mémoire après seulement quelques jours. Vous DEVEZ ÉGALEMENT créer un script cron qui s'exécute toutes les 12 ou peut-être 24 heures qui tue et ré-génère votre script php comme ceci:

killall -3 php
nohup php myscript.php &

Mais que se passerait-il si le script était au milieu d'un travail? Eh bien kill -3 est une interruption, c'est la même chose que de faire un ctrl + c sur la CLI. Votre script php peut intercepter cette interruption et quitter gracieusement en utilisant la bibliothèque PHP pcntl: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Voici un exemple:

function clean_up() {
  GLOBAL $lock;
  mysql_close();
  fclose($lock)
  exit();
}
pcntl_signal(SIGINT, 'clean_up');

L'idée derrière $ lock est que le script PHP peut ouvrir un fichier avec un fopen ("file", "w") ;. Un seul processus peut avoir un verrou d'écriture sur un fichier, vous pouvez donc vous assurer qu'une seule copie de votre script PHP est en cours d'exécution.

Bonne chance!

tour
la source
6

Consultez https://github.com/shaneharter/PHP-Daemon

Il s'agit d'une bibliothèque de démons orientée objet. Il prend en charge des éléments tels que la journalisation et la récupération d'erreur, et prend en charge la création de travailleurs en arrière-plan.

Shane H
la source
3

J'ai récemment eu besoin d'une solution multiplateforme (Windows, Mac et Linux) pour résoudre le problème de l'exécution de scripts PHP en tant que démons. J'ai résolu le problème en écrivant ma propre solution basée sur C ++ et en créant des binaires:

https://github.com/cubiclesoft/service-manager/

Prise en charge complète de Linux (via sysvinit), mais aussi des services Windows NT et du launchd Mac OSX.

Si vous avez juste besoin de Linux, alors quelques autres solutions présentées ici fonctionnent assez bien et, selon la saveur. Il y a aussi Upstart et systemd ces jours-ci, qui ont des solutions de secours pour les scripts sysvinit. Mais la moitié de l'intérêt d'utiliser PHP est qu'il est de nature multiplateforme, donc le code écrit dans le langage a de bonnes chances de fonctionner partout tel quel. Des lacunes commencent à apparaître lorsque certains aspects externes au niveau du système d'exploitation natif entrent dans l'image, tels que les services système, mais vous rencontrerez ce problème avec la plupart des langages de script.

Tenter d'attraper des signaux comme quelqu'un l'a suggéré ici dans le userland PHP n'est pas une bonne idée. Lisez pcntl_signal()attentivement la documentation et vous apprendrez rapidement que PHP gère les signaux en utilisant des méthodes plutôt désagréables (spécifiquement, les «ticks») qui grugent un tas de cycles pour quelque chose de rarement vu par les processus (c'est-à-dire les signaux). La gestion du signal en PHP est également à peine disponible sur les plates-formes POSIX et le support diffère en fonction de la version de PHP. Au départ, cela ressemble à une solution décente, mais elle est loin d'être vraiment utile.

PHP s'est également amélioré sur les problèmes de fuite de mémoire au fil du temps. Il faut encore faire attention (l'analyseur DOM XML a encore tendance à fuir) mais je vois rarement des processus emballés ces jours-ci et le traqueur de bogues PHP est assez silencieux par rapport à autrefois.

CabineSoft
la source
1

Comme d'autres l'ont déjà mentionné, exécuter PHP en tant que démon est assez simple et peut être fait en utilisant une seule ligne de commande. Mais le problème réel est de le faire fonctionner et de le gérer. J'ai eu le même problème il y a un certain temps et bien qu'il y ait déjà beaucoup de solutions disponibles, la plupart d'entre elles ont beaucoup de dépendances ou sont difficiles à utiliser et ne conviennent pas aux usages de base. J'ai écrit un script shell qui peut gérer n'importe quel processus / application, y compris les scripts PHP cli. Il peut être défini comme un cronjob pour démarrer l'application et contiendra l'application et la gérera. S'il est à nouveau exécuté, par exemple via le même cronjob, il vérifie si l'application est en cours d'exécution ou non, s'il le fait, il se ferme simplement et laisse son instance précédente continuer à gérer l'application.

Je l'ai téléchargé sur github, n'hésitez pas à l'utiliser: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Surveille simplement votre application (démarrer, redémarrer, enregistrer, surveiller, etc.). un script générique pour vous assurer que votre appliation continue de fonctionner correctement. Intentionnellement, il utilise le nom du processus au lieu du fichier pid / lock pour éviter tous ses effets secondaires et garder le script aussi simple et réactif que possible, de sorte qu'il fonctionne toujours même lorsque EasyDaemonizer lui-même est redémarré. Caractéristiques

  • Démarre l'application et éventuellement un délai personnalisé pour chaque démarrage
  • S'assure qu'une seule instance est en cours d'exécution
  • Surveille l'utilisation du processeur et redémarre automatiquement l'application lorsqu'elle atteint le seuil défini
  • Configuration d'EasyDeamonizer pour qu'il s'exécute via cron pour le réexécuter s'il est arrêté pour une raison quelconque
  • Consigne son activité
Sina Salek
la source
1

Étendre la réponse d' Emil Ivaov , vous pouvez faire ce qui suit pour fermer STDIN, STDOUT ET STDERROR en 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');

En gros, vous fermez les flux standard pour que PHP n'ait pas de place pour écrire. Les fopenappels suivants définiront l'IO standard sur /dev/null.

J'ai lu ceci dans le livre de Rob Aley - PHP au-delà du web

Raheel
la source
0

J'ai écrit et déployé un simple php-daemon, le code est en ligne ici

https://github.com/jmullee/PhpUnixDaemon

Fonctionnalités: suppression des privilèges, gestion du signal, journalisation

Je l'ai utilisé dans un gestionnaire de file d'attente (cas d'utilisation: déclencher une opération longue depuis une page web, sans faire attendre le php générateur de page, c'est-à-dire lancer une opération asynchrone) https://github.com/jmullee/PhpIPCMessageQueue

jmullee
la source
0

vous pouvez vérifier pm2 ici est, http://pm2.keymetrics.io/

créez un fichier ssh, tel que worker.sh mis dans votre script php que vous allez traiter.

worker.sh

php /path/myscript.php

démarrage du démon

pm2 start worker.sh

Bravo, c'est tout.

Serkan Koch
la source