web-dev-qa-db-fra.com

Exécuter un script php en tant que processus démon

Je dois exécuter un script php en tant que processus démon (attendez les instructions et effectuez des tâches). cron job ne le fera pas pour moi car des actions doivent être entreprises 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 ) qui semble m'aider à gérer les processus de démon, mais aucune mise à jour n'a été mise à jour au cours des 5 dernières années. Je me demande donc. si vous connaissez d'autres alternatives adaptées à mon cas. Toute information sera vraiment appréciée.

138
Beier

Vous pouvez démarrer votre script php à partir de la ligne de commande (c'est-à-dire bash) en utilisant 

Nohup php myscript.php & 

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

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

156
Henrik P. Hessel

Une autre option consiste à utiliser Upstart . Il a été développé à l'origine pour Ubuntu (et est fourni avec lui par défaut), mais il est conçu pour être compatible avec 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 respawns à la fin du script.

Comment le configurer:

Créez un nouveau fichier de script à /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émarrer et arrêter 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 , où j’ai appris cette technique.

150
Jonathan

Si vous pouvez - récupérer une copie de Programmation avancée dans l’environnement UNIX . Tout le chapitre 13 est consacré à la programmation par démon. Les exemples sont en C, mais toutes les fonctions dont vous avez besoin ont des wrappers dans PHP (essentiellement les extensions pcntl et posix ).

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

  1. Appelez umask(0) pour éviter les problèmes d’autorisation.
  2. fork() et que le parent quitte.
  3. Appelez setsid() .
  4. Configurez le 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 fermer normalement).
  5. fork() encore et avoir le parent quitter.
  6. Modifiez le répertoire de travail actuel avec chdir() .
  7. fclose()stdin, stdout et stderr et ne leur écrivez pas. La méthode correcte consiste à les rediriger soit vers /dev/null, soit vers un fichier, mais je n’ai pas trouvé le 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, comme vous utilisez PHP, faites attention aux références cycliques, car le PHP nettoyeur de mémoire, antérieur à 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 des accidents.

47
Emil Ivanov

Avec new systemd vous pouvez créer un service (sur linux basé sur rhel).

Vous devez créer un fichier ou un symlink in /etc/systemd/system/, par exemple. myphpdaemon.service et placez un 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 en utilisant la commande 

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

Le script PHP devrait 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 PHP doit être exécuté une fois dans un cycle (comme un diggest), vous pouvez utiliser un script Shell ou un script 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 cette option, vous devez remplacer le KillMode par mixed par les processus, bash (principal) et php (enfant) être tué.

ExecStart=/app/phpservice/runner.sh phpfile parameter  > /dev/null 2>/dev/null
KillMode=process

Remarque: chaque fois que vous modifiez votre "service myphpdaemon.service", vous devez lancez `systemctl daemon-reload ', mais ne vous inquiétez pas si ce n'est pas le cas, ce sera invité quand est nécessaire.

40
LeonanCarvalho

Je lance un grand nombre de démons PHP. 

Je conviens avec vous que PHP n’est pas le meilleur (ni même un bon) langage pour le faire, mais les démons partagent le code avec les composants Web, c’est donc globalement 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 de fonctionnalités.

  • Démarre automatiquement le démon au redémarrage
  • Redémarrer automatiquement dameon en cas d'échec
  • La journalisation est gérée pour vous, y compris le basculement et l'élagage
  • Interface de gestion: 'svc' et 'svstat'
  • UNIX amical (peut-être pas un plus pour tout le monde)
24
Phil Wallach

Vous pouvez

  1. Utilisez Nohup comme Henrik l'a suggéré.
  2. Utilisez screen et exécutez votre programme PHP comme un processus régulier à l'intérieur de celui-ci. Cela vous donne plus de contrôle que d'utiliser Nohup
  3. Utilisez un démoniseur tel que 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. Ecrivez votre propre wrapper de démonisation comme suggéré par Emil, mais c’est excessif pour IMO.

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

14
Noufal Ibrahim

Il y a plus d'une façon 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 soit exécuté 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 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 très grave problème. Comme vous l'avez dit, le gestionnaire de mémoire de PHP est une foutaise, il a été construit en partant du principe qu'un script ne s'exécute que pendant quelques secondes, puis qu'il existe. Votre script PHP commencera à utiliser des GIGABYTES de mémoire au bout de quelques jours seulement. Vous DEVEZ AUSSI créer un script cron qui s'exécute toutes les 12 ou 24 heures qui tue et reproduit votre script php comme ceci:

killall -3 php
Nohup php myscript.php &

Mais que se passe-t-il si le script était au milieu d'un travail? Bien tuer -3 est une interruption, c'est comme faire ctrl + c sur la CLI. Votre script php peut intercepter cette interruption et sortir normalement grâce à la bibliothèque PHP pcntl: http://php.oregonstate.edu/manual/fr/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 le verrou $ est que le script PHP peut ouvrir un fichier avec un fopen ("fichier", "w") ;. Un seul processus peut avoir un verrou en écriture sur un fichier. Vous pouvez ainsi vous assurer qu'une seule copie de votre script PHP est en cours d'exécution. 

Bonne chance!

11
rook

Kevin van Zonneveld a écrit un très bel article détaillé sur ce point . Dans son exemple, il utilise le package System_Daemon PEAR (dernière publication le 2009-09-02).

10
Alix Axel

Découvrez https://github.com/shaneharter/PHP-Daemon

Ceci est une bibliothèque démon orientée objet. Il prend en charge, entre autres, la journalisation et la récupération d’erreurs, ainsi que la création de travailleurs en arrière-plan. 

6
Shane H

J'ai récemment eu besoin d'une solution multiplate-forme (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 fichiers binaires:

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

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

Si vous avez juste besoin de Linux, quelques-unes des autres solutions présentées ici fonctionnent assez bien et, en fonction de la saveur. Il existe également Upstart et systemd ces jours-ci, qui ont recours aux scripts sysvinit. Cependant, l'utilisation de PHP est en partie basée sur le fait qu'elle est multi-plateforme, donc le code écrit dans le langage a de bonnes chances de fonctionner partout tel quel. Les lacunes commencent à apparaître lorsque certains aspects externes natifs au niveau du système d'exploitation, tels que les services système, apparaissent, mais vous rencontrez ce problème avec la plupart des langages de script.

Essayer d'attraper les signaux comme le suggère quelqu'un ici dans PHP n'est pas une bonne idée. Lisez attentivement la documentation sur pcntl_signal() et vous apprendrez rapidement que PHP traite les signaux en utilisant des méthodes plutôt désagréables (en particulier, les "ticks") qui grignotent un tas de cycles pour quelque chose que les processus (signaux) ont rarement vu. La gestion du signal dans PHP est également à peine disponible sur les plateformes POSIX et le support varie en fonction de la version de PHP. Cela semble initialement une solution décente, mais elle n’est pas vraiment utile.

PHP a également amélioré ses problèmes de fuite de mémoire au fil du temps. Vous devez toujours faire attention (l'analyseur DOM XML a tendance à fuir encore), mais je vois rarement des processus emballés de nos jours et le suivi des bogues PHP est assez silencieux par rapport à il y a bien longtemps.

3
CubicleSoft

Comme d'autres l'ont déjà mentionné, exécuter PHP en tant que démon est assez simple et peut être effectué à l'aide d'une seule ligne de commande. Mais le problème actuel est de le maintenir en fonctionnement et de le gérer. J'ai eu le même problème il y a un certain temps et bien qu'il existe de nombreuses solutions déjà disponibles, la plupart d'entre elles ont de nombreuses dépendances ou sont difficiles à utiliser et ne conviennent pas aux utilisations de base. J'ai écrit un script Shell capable de gérer n'importe quel processus/application, y compris les scripts PHP cli. Il peut être défini en tant que tâche cron pour démarrer l'application. Il contiendra l'application et la gérera. Si elle est exécutée à nouveau, par exemple via le même cronjob, elle vérifie si l'application est en cours d'exécution ou non. Si c'est le cas, fermez-la simplement et laissez son instance précédente continuer à gérer l'application.

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

EasyDeamonizer

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

  • Démarre l'application et éventuellement un délai personnalisé pour chaque démarrage
  • Assure qu'une seule instance est en cours d'exécution
  • Surveille l'utilisation du processeur et redémarre l'application automatiquement lorsqu'elle atteint le seuil défini
  • Configurer EasyDeamonizer pour qu'il s'exécute via cron afin de l'exécuter à nouveau s'il est arrêté pour une raison quelconque
  • Journalise son activité
1
Sina Salek

Étendre Emil Ivaov answer, vous pouvez procéder comme 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 de sorte que PHP n’ait pas d’endroit pour écrire. Les appels fopen suivants définiront la norme IO sur /dev/null.

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

1
Raheel Khan

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

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

worker.sh

php /path/myscript.php

daemon start

pm2 start worker.sh

Cheers, c'est ça.

0
Serkan Koch