Führen Sie das PHP-Skript als Daemon-Prozess aus

154

Ich muss ein PHP-Skript als Daemon-Prozess ausführen (auf Anweisungen warten und Dinge tun). Cron Job wird es für mich nicht tun, da Maßnahmen ergriffen werden müssen, sobald die Anweisung eintrifft. Ich weiß, dass PHP aufgrund von Speicherverwaltungsproblemen nicht wirklich die beste Option für Daemon-Prozesse ist, aber aus verschiedenen Gründen muss ich in diesem Fall PHP verwenden. Ich bin auf ein Tool von libslack namens Daemon ( http://libslack.org/daemon ) gestoßen, das mir bei der Verwaltung von Daemon-Prozessen zu helfen scheint, aber in den letzten 5 Jahren gab es keine Updates. Ich frage mich, ob Sie welche kennen andere Alternativen für meinen Fall geeignet. Alle Informationen werden sehr geschätzt.

Beier
quelle
2
Bitte überprüfen Sie meine Antwort. danke
Henrik P. Hessel
1
Ich bin auf diesen Beitrag gonzalo123.com/2010/05/23/… gestoßen, von dem ich glaube, dass er sowohl entspannend als auch stabil ist.
Teson
Es ist sehr einfach mit systemd
LeonanCarvalho

Antworten:

167

Sie können Ihr PHP-Skript über die Befehlszeile (dh Bash) mit starten

nohup php myscript.php &

Das &stellt Ihren Prozess in den Hintergrund.

Bearbeiten:
Ja, es gibt einige Nachteile, aber nicht möglich zu kontrollieren? Das ist einfach falsch.
Ein einfaches kill processidwird es aufhalten. Und es ist immer noch die beste und einfachste Lösung.

Henrik P. Hessel
quelle
Wenn das Terminal vorhanden ist, wird der Prozess NICHT beendet. Deshalb gibt es den Befehl "nohup". Ich verwende seit Jahren ein PHP-Skript als Daemon auf allen Servern wie diesem. Es gibt vielleicht eine bessere Lösung, aber dies ist die schnellste.
CDR
27
Dadurch wird der Dämon nicht neu gestartet, wenn er fehlschlägt, und es gibt keine einfache Möglichkeit, den Dämon überhaupt zu verwalten.
Phil Wallach
6
Ich stimme dem zu, was hier gesagt wurde - das ist eine schlechte Lösung. Sie sollten ein Init-Skript aus mehreren Gründen erstellen: 1) Das Init-Skript wird beim Start automatisch gestartet. 2) Sie können den Daemon mit den Befehlen start / stop / restart verwalten. Hier ist ein Beispiel von Servefault: serverfault.com/questions/229759/…
Simian
1
Hey Leute ... es scheint mir nohupund &macht genau das Gleiche: den gestarteten Prozess von der aktuellen Intance der Shell zu lösen. Warum brauche ich beide? Kann ich nicht einfach machen php myscript.php &oder nohup myscript.php?? Danke
Nourdine
1
Wenn das Skript in stdout schreibt (über echo oder var_dump), können Sie diese Informationen mit einer Protokolldatei wie der folgenden abfangen:nohup php myscript.php > myscript.log &
Mischa
166

Eine weitere Option ist die Verwendung von Upstart . Es wurde ursprünglich für Ubuntu entwickelt (und wird standardmäßig mitgeliefert), ist jedoch für alle Linux-Distributionen geeignet.

Dieser Ansatz ähnelt Supervisord und daemontools , da der Daemon beim Systemstart automatisch gestartet und beim Abschluss des Skripts erneut gestartet wird.

So richten Sie es ein:

Erstellen Sie eine neue Skriptdatei unter /etc/init/myphpworker.conf. Hier ist ein Beispiel:

# 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

Starten und Stoppen Ihres Daemons:

sudo service myphpworker start
sudo service myphpworker stop

Überprüfen Sie, ob Ihr Daemon ausgeführt wird:

sudo service myphpworker status

Vielen Dank

Ein großes Dankeschön an Kevin van Zonneveld , von dem ich diese Technik gelernt habe.

Jonathan
quelle
2
Das mag ich. Ich frage mich nur, ob es möglich ist, mehrere Mitarbeiter gleichzeitig zu haben. Ich habe nur das Problem, dass ein Arbeiter nicht mehr ausreicht.
Manuel
1
Wird dies beim Systemstart automatisch ausgeführt?
Slier
2
Sudo "Service Myphpworker Start" hat bei mir nicht funktioniert. Ich habe "sudo start myphpworker" verwendet und es funktioniert perfekt
Matt Sich
3
@Pradeepta Das liegt daran, dass es einen Fehler in der Post gibt - ich bin nicht genau sicher, was (und habe dies nicht getestet), aber ich denke, dass dies sudo service myphpworker start/stop/statusnur mit Diensten funktioniert, die /etc/init.dnicht in Upstart-Diensten enthalten sind. @ matt-sich scheint die richtige Syntax aufgedeckt zu haben. Eine andere Option ist die Verwendung von Gearman oder Resque, die Hintergrundverarbeitung und Desamonisierung ermöglicht.
km
3
Ubuntu selbst verwendet Systemd anstelle von Upstart: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai
72

Mit new systemd können Sie einen Service erstellen.

Sie müssen eine Datei oder einen Symlink in erstellen /etc/systemd/system/, z. myphpdaemon.service und platzieren Sie Inhalte wie diesen, myphpdaemon ist der Name des Dienstes:

[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

Mit dem Befehl können Sie die Dienste starten, abrufen, neu starten und stoppen

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

Das PHP-Skript sollte eine Art "Schleife" haben, um weiter ausgeführt zu werden.

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

Arbeitsbeispiel:

[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

Wenn Ihre PHP-Routine einmal in einem Zyklus ausgeführt werden soll (wie ein Diggest), sollten Sie ein Shell- oder Bash-Skript verwenden, um anstelle von PHP direkt in die systemd-Dienstdatei aufgerufen zu werden, zum Beispiel:

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

Wenn Sie diese Option gewählt haben, sollten Sie den KillMode in mixedProzesse ändern , Bash (Haupt) und PHP (Kind) werden beendet.

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.

Hinweis: Jedes Mal, wenn Sie Ihren "myphpdaemon.service" ändern, müssen Sie "systemctl daemon-reload" ausführen. Wenn Sie dies nicht tun, wird es benachrichtigt, wenn dies erforderlich ist.

LeonanCarvalho
quelle
7
Unterschätzte Antwort. Du hast meine +1.
Gergely Lukacsy
2
Genial. Ich wünschte, wir könnten auch herzliche Antworten geben, da dies nicht auf dieser Seite vergraben werden sollte.
Justin
1
Sie sollten die systemctl status <your_service_name> -lAusgabe überprüfen , sie gibt Ihnen einen Hinweis darauf, was passiert.
LeonanCarvalho
1
@LeandroTupone MySQL und Memcached waren eine Demonstration der Verwendung von Dienstabhängigkeiten, die nicht erforderlich sind.
LeonanCarvalho
3
Dies sollte die akzeptierte Antwort wirklich ersetzen, da es jetzt 2019 ist.
Gewürz
47

Wenn Sie können - holen Sie sich eine Kopie der erweiterten Programmierung in der UNIX-Umgebung . Das gesamte Kapitel 13 ist der Daemon-Programmierung gewidmet. Beispiele sind in C, aber alle Funktionen, die Sie benötigen, haben Wrapper in PHP (im Grunde die Erweiterungen pcntl und posix ).

Mit wenigen Worten: Das Schreiben eines Daemons (dies ist nur unter * nix-basierten Betriebssystemen möglich - Windows verwendet Dienste) sieht folgendermaßen aus:

  1. Rufen Sie umask(0)an, um Berechtigungsprobleme zu vermeiden.
  2. fork() und lassen Sie die Eltern beenden.
  3. Rufen Sie an setsid().
  4. Setup-Signalverarbeitung von SIGHUP(normalerweise wird dies ignoriert oder verwendet, um dem Dämon zu signalisieren, seine Konfiguration neu zu laden) und SIGTERM(um den Prozess anzuweisen, ordnungsgemäß zu beenden).
  5. fork() wieder und lassen Sie die Eltern beenden.
  6. Ändern Sie das aktuelle Arbeitsverzeichnis mit chdir() .
  7. fclose() stdin, stdout Und stderrund schreiben Sie nicht zu ihnen. Der richtige Weg ist, diese entweder /dev/nulloder in eine Datei umzuleiten , aber ich konnte in PHP keinen Weg finden, dies zu tun. Wenn Sie den Daemon starten, ist es möglich, sie mithilfe der Shell umzuleiten (Sie müssen selbst herausfinden, wie das geht, ich weiß nicht :).
  8. Tun Sie Ihre Arbeit!

Da Sie PHP verwenden, achten Sie auch auf zyklische Referenzen, da der PHP-Garbage Collector vor PHP 5.3 keine Möglichkeit hat, diese Referenzen zu sammeln, und der Prozess Speicherverlust verursacht, bis er schließlich abstürzt.

Emil Ivanov
quelle
1
Danke für die Information. Es sieht so aus, als ob das Daemon-Programm von libslack so ziemlich die gesamte Vorbereitungsarbeit erledigt, wie Sie es erwähnt haben. Ich denke, ich bleibe vorerst dabei, bis ich andere gute Alternativen finde.
Beier
1
Fand diesen Beitrag, erwartete Code zum Kopieren und Einfügen in eine beschissene alte Anwendung, die stdin usw. nicht schließen kann, war enttäuscht. : p
ThiefMaster
1
Warum (5) wieder gabeln ()?
TheFox
Arbeitete für mich - tolle Arbeit!
Gautam Sharma
Für zukünftige Leser, die fragen, warum sie zweimal gabeln sollten: stackoverflow.com/questions/881388/… - TL; DR: Verhindert Zombies.
Ghedipunk
24

Ich verwende eine große Anzahl von PHP-Daemons.

Ich stimme Ihnen zu, dass PHP nicht die beste (oder sogar eine gute) Sprache dafür ist, aber die Daemons teilen Code mit den webbasierten Komponenten, sodass es insgesamt eine gute Lösung für uns ist.

Wir verwenden dafür Daemontools. Es ist intelligent, sauber und zuverlässig. Tatsächlich verwenden wir es, um alle unsere Dämonen auszuführen.

Sie können dies unter http://cr.yp.to/daemontools.html überprüfen .

BEARBEITEN: Eine kurze Liste der Funktionen.

  • Startet den Daemon beim Neustart automatisch
  • Starten Sie dameon bei einem Fehler automatisch neu
  • Die Protokollierung wird für Sie erledigt, einschließlich Rollover und Bereinigung
  • Verwaltungsoberfläche: 'svc' und 'svstat'
  • UNIX-freundlich (vielleicht kein Plus für alle)
Phil Wallach
quelle
Auch aus den Repositories installierbar, zB in apt!
Kzqai
14

Sie können

  1. Verwenden Sie, nohupwie Henrik vorgeschlagen hat.
  2. Verwenden Sie screenIhr PHP-Programm und führen Sie es als regulären Prozess aus. Dies gibt Ihnen mehr Kontrolle als die Verwendung nohup.
  3. Verwenden Sie einen Daemoniser wie http://supervisord.org/ (er ist in Python geschrieben, kann jedoch jedes Befehlszeilenprogramm dämonisieren und Ihnen eine Fernbedienung zur Verwaltung geben).
  4. Schreiben Sie Ihren eigenen Daemonise-Wrapper, wie Emil vorgeschlagen hat, aber es ist übertrieben, IMO.

Ich würde die einfachste Methode empfehlen (Bildschirm meiner Meinung nach) und dann, wenn Sie mehr Features oder Funktionen wünschen, zu komplexeren Methoden übergehen.

Noufal Ibrahim
quelle
Könnten Sie eine ähnliche Supervisord-Konfiguration bereitstellen?
Alix Axel
11

Es gibt mehr als einen Weg, um dieses Problem zu lösen.

Ich kenne die Einzelheiten nicht, aber vielleicht gibt es eine andere Möglichkeit, den PHP-Prozess auszulösen. Wenn Sie beispielsweise den Code zum Ausführen basierend auf Ereignissen in einer SQL-Datenbank benötigen, können Sie einen Trigger einrichten, um Ihr Skript auszuführen. Dies ist unter PostgreSQL ganz einfach: http://www.postgresql.org/docs/current/static/external-pl.html .

Ehrlich gesagt denke ich, dass Ihre beste Wette darin besteht, einen Damon-Prozess mit nohup zu erstellen. Mit nohup kann der Befehl auch nach dem Abmelden des Benutzers weiter ausgeführt werden:

nohup php myscript.php &

Es gibt jedoch ein sehr ernstes Problem. Wie Sie sagten, ist der Speichermanager von PHP ein vollständiger Müll. Er wurde unter der Annahme erstellt, dass ein Skript nur einige Sekunden lang ausgeführt wird und dann vorhanden ist. Ihr PHP-Skript verwendet bereits nach wenigen Tagen GIGABYTES des Speichers. Sie MÜSSEN AUCH ein Cron-Skript erstellen, das alle 12 oder vielleicht 24 Stunden ausgeführt wird und Ihr PHP-Skript wie folgt beendet und erneut erzeugt:

killall -3 php
nohup php myscript.php &

Aber was ist, wenn das Drehbuch mitten in einem Job war? Nun, kill -3 ist ein Interrupt, das gleiche wie Strg + C auf der CLI. Ihr PHP-Skript kann diesen Interrupt abfangen und mithilfe der PHP-PCNL-Bibliothek ordnungsgemäß beenden: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Hier ist ein Beispiel:

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

Die Idee hinter der $ lock ist, dass das PHP-Skript eine Datei mit einem fopen ("file", "w") öffnen kann. Nur ein Prozess kann eine Schreibsperre für eine Datei haben. Mit dieser Option können Sie sicherstellen, dass nur eine Kopie Ihres PHP-Skripts ausgeführt wird.

Viel Glück!

Turm
quelle
6

Überprüfen Sie https://github.com/shaneharter/PHP-Daemon

Dies ist eine objektorientierte Daemon-Bibliothek. Es bietet integrierte Unterstützung für Protokollierung und Fehlerbehebung sowie Unterstützung für die Erstellung von Hintergrundarbeitern.

Shane H.
quelle
3

Vor kurzem brauchte ich eine plattformübergreifende Lösung (Windows, Mac und Linux) für das Problem, PHP-Skripte als Daemons auszuführen. Ich habe das Problem gelöst, indem ich meine eigene C ++ - basierte Lösung geschrieben und Binärdateien erstellt habe:

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

Volle Unterstützung für Linux (über Sysvinit), aber auch Windows NT-Dienste und Mac OSX Launchd.

Wenn Sie nur Linux benötigen, funktionieren einige der anderen hier vorgestellten Lösungen gut genug und je nach Geschmack. Es gibt heutzutage auch Upstart und Systemd, die auf Sysvinit-Skripte zurückgreifen. Die Hälfte der Verwendung von PHP besteht jedoch darin, dass es plattformübergreifend ist, sodass in der Sprache geschriebener Code eine gute Chance hat, überall so zu arbeiten, wie er ist. Mängel treten auf, wenn bestimmte Aspekte des externen nativen Betriebssystems wie Systemdienste in das Bild eingehen. Bei den meisten Skriptsprachen tritt dieses Problem jedoch auf.

Der Versuch, Signale zu fangen, wie es hier im PHP-Userland vorgeschlagen wurde, ist keine gute Idee. Lesen Sie die Dokumentation pcntl_signal()sorgfältig durch und Sie werden schnell feststellen, dass PHP Signale mit einigen eher unangenehmen Methoden (insbesondere „Ticks“) verarbeitet, die eine Reihe von Zyklen für etwas zerkauen, das von Prozessen selten gesehen wird (dh Signale). Die Signalverarbeitung in PHP ist auch auf POSIX-Plattformen kaum verfügbar und die Unterstützung unterscheidet sich je nach PHP-Version. Es klingt zunächst nach einer anständigen Lösung, ist aber nicht wirklich nützlich.

PHP hat sich im Laufe der Zeit auch in Bezug auf Speicherverlustprobleme verbessert. Sie müssen immer noch vorsichtig sein (der DOM-XML-Parser neigt dazu, immer noch zu lecken), aber ich sehe heutzutage selten außer Kontrolle geratene Prozesse und der PHP-Bug-Tracker ist im Vergleich zu früher ziemlich leise.

CubicleSoft
quelle
1

Wie andere bereits erwähnt haben, ist das Ausführen von PHP als Daemon recht einfach und kann mit einer einzigen Befehlszeile erfolgen. Das eigentliche Problem besteht jedoch darin, es am Laufen zu halten und zu verwalten. Ich hatte vor einiger Zeit das gleiche Problem und obwohl es bereits viele Lösungen gibt, haben die meisten viele Abhängigkeiten oder sind schwierig zu verwenden und nicht für grundlegende Verwendungen geeignet. Ich habe ein Shell-Skript geschrieben, das jeden Prozess / jede Anwendung verwalten kann, einschließlich PHP-CLI-Skripten. Es kann als Cronjob zum Starten der Anwendung festgelegt werden und enthält die Anwendung und verwaltet sie. Wenn es erneut ausgeführt wird, beispielsweise über denselben Cronjob, prüft es, ob die App ausgeführt wird oder nicht. Wenn dies der Fall ist, wird es einfach beendet und die vorherige Instanz kann die Anwendung weiter verwalten.

Ich habe es auf github hochgeladen und kann es gerne verwenden: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Überwacht einfach Ihre Anwendung (Starten, Neustarten, Protokollieren, Überwachen usw.). Ein generisches Skript, um sicherzustellen, dass Ihre Anwendung weiterhin ordnungsgemäß ausgeführt wird. Absichtlich wird der Prozessname instread der PID / Lock-Datei verwendet, um alle Nebenwirkungen zu vermeiden und das Skript so einfach und schnell wie möglich zu halten, sodass es auch dann immer funktioniert, wenn EasyDaemonizer selbst neu gestartet wird. Eigenschaften

  • Startet die Anwendung und optional eine angepasste Verzögerung für jeden Start
  • Stellt sicher, dass nur eine Instanz ausgeführt wird
  • Überwacht die CPU-Auslastung und startet die App automatisch neu, wenn sie den definierten Schwellenwert erreicht
  • Festlegen, dass EasyDeamonizer über cron ausgeführt wird, um es erneut auszuführen, wenn es aus irgendeinem Grund angehalten wird
  • Protokolliert seine Aktivität
Sina Salek
quelle
1

Erweitern Sie die Antwort von Emil Ivaov . Sie können Folgendes tun, um STDIN, STDOUT UND STDERROR in PHP zu schließen

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');

Grundsätzlich schließen Sie die Standard-Streams, damit PHP keinen Platz zum Schreiben hat. Die folgenden fopenAufrufe setzen die Standard-E / A auf /dev/null.

Ich habe dies aus dem Buch von Rob Aley - PHP jenseits des Webs gelesen

Raheel
quelle
0

Ich habe einen einfachen PHP-Daemon geschrieben und bereitgestellt, Code ist hier online

https://github.com/jmullee/PhpUnixDaemon

Funktionen: Löschen von Berechtigungen, Signalverarbeitung, Protokollierung

Ich habe es in einem Warteschlangen-Handler verwendet (Anwendungsfall: Auslösen einer längeren Operation von einer Webseite, ohne dass die seitengenerierende PHP warten muss, dh eine asynchrone Operation starten) https://github.com/jmullee/PhpIPCMessageQueue

jmullee
quelle
0

Sie können pm2 hier überprüfen, http://pm2.keymetrics.io/

Erstellen Sie eine SSH-Datei, z. B. worker.sh, die in Ihr PHP-Skript eingefügt wird, mit dem Sie sich befassen werden.

worker.sh

php /path/myscript.php

Daemon starten

pm2 start worker.sh

Prost, das ist es.

Serkan Koch
quelle