Esegui script php come processo daemon


154

Ho bisogno di eseguire uno script php come processo daemon (attendere istruzioni e fare cose). cron job non lo farà per me perché le azioni devono essere intraprese non appena arrivano le istruzioni. So che PHP non è davvero la migliore opzione per i processi daemon a causa di problemi di gestione della memoria, ma per vari motivi devo usare PHP in questo caso. Mi sono imbattuto in uno strumento di libslack chiamato Daemon ( http://libslack.org/daemon ) che mi aiuta a gestire i processi demoni, ma non ci sono stati aggiornamenti negli ultimi 5 anni, quindi mi chiedo se ne conosci alcuni altre alternative adatte al mio caso. Qualsiasi informazione sarà molto apprezzata.


2
per favore controlla la mia risposta. grazie
Henrik P. Hessel,


1
Mi sono imbattuto in questo post gonzalo123.com/2010/05/23/… che credo sia rilassante e stabile.
Teson,

È molto facile da fare con systemd
LeonanCarvalho

Risposte:


167

È possibile avviare lo script php dalla riga di comando (ovvero bash) utilizzando

nohup php myscript.php &

la &mette il processo in background.

Modifica:
Sì, ci sono alcuni svantaggi, ma non è possibile controllare? È solo sbagliato.
Un semplice kill processidlo fermerà. Ed è ancora la soluzione migliore e più semplice.


Se il terminale esiste, il processo NON verrà chiuso. Ecco perché il comando "nohup" è lì. Da anni uso uno script PHP come demone su tutti i server come questo. Potrebbe esserci una soluzione migliore, ma questa è la più veloce.
CDR,

27
Questo non riavvierà il demone se fallisce e non esiste alcun modo semplice per gestire il demone.
Phil Wallach,

6
Sono d'accordo con ciò che è stato detto qui-- questa è una cattiva soluzione. Dovresti creare uno script init per un paio di motivi: 1) Lo script Init viene avviato automaticamente all'avvio 2) Puoi gestire il demone con i comandi start / stop / restart. Ecco un esempio da servefault: serverfault.com/questions/229759/…
Simian

1
ciao ragazzi ... mi sembra nohupe &fa la stessa cosa: staccare il processo avviato dall'intenzione attuale della shell. Perché ho bisogno di entrambi? Non posso semplicemente fare php myscript.php &o nohup myscript.php?? Grazie
nourdine il

1
Se lo script scrive su stdout (tramite echo o var_dump), puoi prendere queste informazioni con un file di registro come questo:nohup php myscript.php > myscript.log &
Mischa,

167

Un'altra opzione è utilizzare Upstart . È stato originariamente sviluppato per Ubuntu (e viene fornito in pacchetto con esso per impostazione predefinita), ma è pensato per essere adatto a tutte le distribuzioni Linux.

Questo approccio è simile a Supervisord e daemontools , in quanto avvia automaticamente il demone all'avvio del sistema e ricompare al completamento dello script.

Come configurarlo:

Crea un nuovo file di script in /etc/init/myphpworker.conf. Ecco un esempio:

# 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

Avvio e arresto del demone:

sudo service myphpworker start
sudo service myphpworker stop

Controlla se il tuo demone è in esecuzione:

sudo service myphpworker status

Grazie

Un grande ringraziamento a Kevin van Zonneveld , da cui ho imparato questa tecnica.


2
amando questo. Mi chiedo, è possibile avere più lavoratori simultanei? Ho solo il problema che un lavoratore non è più abbastanza.
Manuel,

1
sarà eseguito automaticamente all'avvio del sistema?
slier

2
Il "servizio myphpworker start" del Sudo non ha funzionato per me. Ho usato "sudo start myphpworker" e funziona perfettamente
Matt Sich,

3
@Pradeepta Questo è perché c'è un errore nel post - Non sono esattamente sicuro di cosa (e non l'ho testato), ma penso che sudo service myphpworker start/stop/statusfunzioni solo con servizi che non sono in /etc/init.dservizi upstart. @ matt-sich sembra aver scoperto la sintassi corretta. Un'altra opzione è utilizzare Gearman o Resque, che consente l'elaborazione in background e la deamonizzazione.
CKM

3
Ubuntu stesso sta passando all'utilizzo di systemd invece di upstart: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai

72

Con il nuovo systemd è possibile creare un servizio.

È necessario creare un file o un link simbolico a /etc/systemd/system/, per esempio. myphpdaemon.service e posiziona contenuti come questo, myphpdaemon sarà il nome del servizio:

[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

Sarai in grado di avviare, ottenere lo stato, riavviare e interrompere i servizi utilizzando il comando

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

Lo script PHP dovrebbe avere una sorta di "loop" per continuare l'esecuzione.

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

Esempio funzionante:

[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

Se la routine PHP deve essere eseguita una volta in un ciclo (come un digest), è possibile utilizzare uno script shell o bash per essere richiamato direttamente nel file di servizio systemd anziché in PHP, ad esempio:

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

Se scegli questa opzione, dovresti cambiare KillMode in mixedprocessi, bash (main) e PHP (child) verranno uccisi.

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.

Nota: ogni volta che si modifica "myphpdaemon.service" è necessario eseguire `systemctl daemon-reload ', ma non preoccuparsi, in caso contrario si riceverà un avviso.


7
Risposta sottovalutata. Hai il mio +1.
Gergely Lukacsy,

2
Eccezionale. Vorrei che anche noi potessimo rispondere con il cuore perché questo non dovrebbe essere sepolto in questa pagina.
Justin,

1
Dovresti controllare l' systemctl status <your_service_name> -loutput, ti darà un'idea di cosa sta succedendo.
LeonanCarvalho,

1
@LeandroTupone MySQL e Memcached sono stati una dimostrazione di come utilizzare le dipendenze del servizio non è necessario.
LeonanCarvalho,

3
Questo dovrebbe davvero sostituire la risposta accettata visto che è ora il 2019.
spezia l'

47

Se puoi, prendi una copia della Programmazione avanzata nell'ambiente UNIX . L'intero capitolo 13 è dedicato alla programmazione dei demoni. Gli esempi sono in C, ma tutte le funzioni necessarie hanno wrapper in PHP (sostanzialmente le estensioni pcntl e posix ).

In poche parole - scrivere un demone (questo è possibile solo su sistemi operativi basati su * nix - Windows utilizza i servizi) è come questo:

  1. Chiama umask(0)per evitare problemi di autorizzazione.
  2. fork() e avere l'uscita padre.
  3. Chiama setsid().
  4. Elaborazione del segnale di installazione di SIGHUP(di solito questo viene ignorato o utilizzato per segnalare al demone di ricaricare la sua configurazione) e SIGTERM(per dire al processo di uscire con grazia).
  5. fork() di nuovo e avere l'uscita genitore.
  6. Cambia la directory di lavoro corrente con chdir().
  7. fclose() stdin, stdoutE stderre non scrivere a loro. Il modo corretto è reindirizzare quelli su uno /dev/nullo su un file, ma non sono riuscito a trovare un modo per farlo in PHP. È possibile quando avvii il demone per reindirizzarli usando la shell (dovrai scoprire tu stesso come farlo, non lo so :).
  8. Fai il tuo lavoro!

Inoltre, poiché stai usando PHP, fai attenzione ai riferimenti ciclici, poiché il garbage collector di PHP, prima di PHP 5.3, non ha modo di raccogliere quei riferimenti e il processo perde memoria, fino a quando non si blocca.


1
Grazie per le informazioni. Sembra che il programma demone di libslack faccia praticamente tutto il lavoro di preparazione come hai menzionato. Penso che per ora continuerò fino a quando non troverò altre buone alternative.
Beier,

1
Trovato questo post, il codice previsto per copiare e incollare in una vecchia applicazione scadente che non riesce a chiudere stdin ecc., È stato deluso. : p
ThiefMaster

1
Perché (5) fork () di nuovo?
TheFox,

Ha funzionato per me - ottimo lavoro!
Gautam Sharma,

Per i futuri lettori che chiedono perché biforcare due volte: stackoverflow.com/questions/881388/… - TL; DR: impedisce gli zombi.
Ghedipunk,

24

Corro un gran numero di demoni PHP.

Sono d'accordo con te sul fatto che PHP non è il miglior (o addirittura un buon) linguaggio per farlo, ma i demoni condividono il codice con i componenti rivolti al web, quindi nel complesso è una buona soluzione per noi.

Usiamo daemontools per questo. È intelligente, pulito e affidabile. In effetti lo usiamo per far funzionare tutti i nostri demoni.

Puoi verificarlo su http://cr.yp.to/daemontools.html .

EDIT: un breve elenco di funzionalità.

  • Avvia automaticamente il demone al riavvio
  • Riavvia automaticamente dameon in caso di errore
  • La registrazione viene gestita per te, inclusi rollover e potatura
  • Interfaccia di gestione: "svc" e "svstat"
  • UNIX amichevole (forse non un vantaggio per tutti)

Installabile anche dai repository, ad es. In apt!
Kzqai,

14

Puoi

  1. Usa nohupcome suggerito Henrik.
  2. Usa screened esegui il tuo programma PHP come un normale processo al suo interno. Questo ti dà più controllo rispetto all'uso nohup.
  3. Usa un demone come http://supervisord.org/ (è scritto in Python ma può demonizzare qualsiasi programma a riga di comando e darti un controllo remoto per gestirlo).
  4. Scrivi il tuo demone wrapper come Emil ha suggerito ma è eccessivo IMO.

Consiglierei il metodo più semplice (schermo secondo me) e quindi se vuoi più funzionalità o funzionalità, passa a metodi più complessi.


Potresti fornire una configurazione di supervisord simile?
Alix Axel,

11

Esiste più di un modo per risolvere questo problema.

Non conosco i dettagli ma forse c'è un altro modo per innescare il processo PHP. Ad esempio, se è necessario eseguire il codice in base agli eventi in un database SQL, è possibile impostare un trigger per eseguire lo script. Questo è davvero facile da fare con PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html .

Onestamente penso che la tua scommessa migliore sia quella di creare un processo Damon usando nohup. nohup consente al comando di continuare l'esecuzione anche dopo che l'utente si è disconnesso:

nohup php myscript.php &

C'è tuttavia un problema molto serio. Come hai detto, il gestore della memoria di PHP è completamente spazzatura, è stato creato partendo dal presupposto che uno script è in esecuzione solo per pochi secondi e quindi esiste. Lo script PHP inizierà a utilizzare GIGABYTES di memoria dopo solo pochi giorni. DEVI ANCHE creare uno script cron che viene eseguito ogni 12 o forse 24 ore che uccide e ricompone il tuo script php in questo modo:

killall -3 php
nohup php myscript.php &

E se la sceneggiatura fosse nel bel mezzo di un lavoro? Bene, uccidere -3 è un interrupt, è lo stesso che fare un ctrl + c sulla CLI. Il tuo script php può catturare questo interrupt ed uscire con garbo usando la libreria pcntl di PHP: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Ecco un esempio:

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

L'idea alla base di $ lock è che lo script PHP può aprire un file con un fopen ("file", "w") ;. Solo un processo può avere un blocco in scrittura su un file, quindi usando questo puoi assicurarti che sia in esecuzione solo una copia del tuo script PHP.

In bocca al lupo!



6

Dai un'occhiata a https://github.com/shaneharter/PHP-Daemon

Questa è una libreria di demoni orientata agli oggetti. Ha un supporto integrato per cose come la registrazione e il recupero degli errori e ha il supporto per la creazione di lavoratori in background.


3

Di recente avevo bisogno di una soluzione multipiattaforma (Windows, Mac e Linux) al problema di eseguire script PHP come demoni. Ho risolto il problema scrivendo la mia soluzione basata su C ++ e creando binari:

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

Supporto completo per Linux (tramite sysvinit), ma anche per i servizi di Windows NT e Mac OSX lanciato.

Se hai solo bisogno di Linux, allora un paio delle altre soluzioni presentate qui funzionano abbastanza bene e, a seconda del sapore. Esistono anche Upstart e systemd in questi giorni, che hanno fallback negli script sysvinit. Ma metà del punto di usare PHP è che è di natura multipiattaforma, quindi il codice scritto nella lingua ha buone possibilità di lavorare ovunque così com'è. Le carenze iniziano a manifestarsi quando alcuni aspetti esterni a livello di sistema operativo nativo entrano in scena come i servizi di sistema, ma questo problema si presenta con la maggior parte dei linguaggi di scripting.

Tentare di catturare segnali come qualcuno qui suggerito nella zona utente di PHP non è una buona idea. Leggi pcntl_signal()attentamente la documentazione e imparerai rapidamente che PHP gestisce i segnali usando alcuni metodi piuttosto spiacevoli (in particolare, "tick") che masticano un sacco di cicli per qualcosa che raramente viene visto dai processi (cioè segnali). La gestione del segnale in PHP è anche a malapena disponibile solo su piattaforme POSIX e il supporto differisce in base alla versione di PHP. Inizialmente sembra una soluzione decente ma non è molto utile.

PHP ha anche migliorato i problemi di perdita di memoria con il passare del tempo. Devi ancora stare attento (il parser DOM XML tende a perdere ancora) ma raramente vedo processi in fuga in questi giorni e il tracker di bug PHP è piuttosto silenzioso rispetto ai giorni passati.


1

Come altri hanno già detto, eseguire PHP come demone è abbastanza semplice e può essere fatto usando una singola riga di comando. Ma il vero problema è mantenerlo in esecuzione e gestirlo. Ho avuto lo stesso problema un po 'di tempo fa e sebbene ci siano molte soluzioni già disponibili, la maggior parte di esse ha molte dipendenze o sono difficili da usare e non adatte agli usi di base. Ho scritto uno script di shell in grado di gestire qualsiasi processo / applicazione compresi gli script di cli di PHP. Può essere impostato come cronjob per avviare l'applicazione e conterrà l'applicazione e la gestirà. Se viene eseguito di nuovo, ad esempio tramite lo stesso cronjob, controlla se l'app è in esecuzione o meno, se lo fa semplicemente esce e lascia che la sua istanza precedente continui a gestire l'applicazione.

L'ho caricato su github, sentitevi liberi di usarlo: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Controlla semplicemente la tua applicazione (avvio, riavvio, registro, monitoraggio, ecc.). uno script generico per assicurarsi che la tua appliation continui a funzionare correttamente. Usa intenzionalmente il nome del processo indicato dal file pid / lock per prevenire tutti i suoi effetti collaterali e mantenere lo script il più semplice e agevole possibile, quindi funziona sempre anche quando EasyDaemonizer stesso viene riavviato. Caratteristiche

  • Avvia l'applicazione e, facoltativamente, un ritardo personalizzato per ogni avvio
  • Si assicura che sia in esecuzione solo un'istanza
  • Monitora l'utilizzo della CPU e riavvia l'app automaticamente quando raggiunge la soglia definita
  • Impostazione di EasyDeamonizer per l'esecuzione tramite cron per eseguirlo nuovamente se viene interrotto per qualsiasi motivo
  • Registra la sua attività

1

Estendendo la risposta di Emil Ivaov , puoi fare quanto segue per chiudere STDIN, STDOUT E STDERROR in 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');

Fondamentalmente chiudi i flussi standard in modo che PHP non abbia spazio per scrivere. Le seguenti fopenchiamate imposteranno l'IO standard su /dev/null.

L'ho letto dal libro di Rob Aley - PHP oltre il web



0

puoi controllare pm2 qui, http://pm2.keymetrics.io/

crea un file ssh, come worker.sh, inseriscilo nel tuo script php di cui ti occuperai.

worker.sh

php /path/myscript.php

inizio del demone

pm2 start worker.sh

Saluti, questo è tutto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.