linux <? - Esegui script php come processo daemon




<?= <! (11)

Puoi

  1. Usa nohup come suggerito da Henrik.
  2. Usa lo screen ed esegui il tuo programma PHP come un normale processo al suo interno. Questo ti dà più controllo dell'uso di nohup .
  3. Usa un daemoniser come Supervisord (è scritto in Python ma può demonizzare qualsiasi programma da riga di comando e darti un telecomando per gestirlo).
  4. Scrivi il tuo wrapper daemonise come suggerito da Emil, ma è IMO eccessivo.

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

Ho bisogno di eseguire uno script php come processo daemon (attendere le 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 demone a causa di problemi di gestione della memoria, ma a causa di 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 demone, ma non ci sono stati aggiornamenti negli ultimi 5 anni, quindi mi chiedo se ne sai qualcosa altre alternative adatte al mio caso. Qualsiasi informazione sarà molto apprezzata.


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

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

Pieno supporto per Linux (tramite sysvinit), ma anche servizi Windows NT e Mac OSX launchd.

Se hai solo bisogno di Linux, allora un paio delle altre soluzioni presentate qui funzionano abbastanza bene e, a seconda del gusto. Al giorno d'oggi ci sono anche Upstart e systemd, che hanno ripercussioni sugli script sysvinit. Ma metà del punto di utilizzo di PHP è che è di natura multipiattaforma, quindi il codice scritto nella lingua ha buone possibilità di funzionare ovunque così com'è. Le carenze iniziano a comparire quando alcuni aspetti esterni a livello di sistema operativo nativo entrano nell'immagine, come i servizi di sistema, ma il problema si verifica con la maggior parte dei linguaggi di scripting.

Tentare di catturare segnali come qualcuno qui suggerito nel campo utente di PHP non è una buona idea. Leggete attentamente la documentazione su pcntl_signal() e imparerete rapidamente che il PHP gestisce i segnali usando alcuni metodi piuttosto sgradevoli (in particolare "tick") che masticano un mucchio di cicli per qualcosa che raramente viene visto dai processi (cioè segnali). Anche la gestione dei segnali in PHP è a malapena disponibile sulle piattaforme POSIX e supporta differenze in base alla versione di PHP. Inizialmente suona come una soluzione decente, ma non è molto utile.

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


C'è più di un modo per risolvere questo problema.

Non conosco le specifiche ma forse c'è un altro modo per attivare 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 è veramente facile da fare sotto PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html .

Onestamente penso che la cosa migliore da fare sia creare un processo Damon usando nohup. nohup consente al comando di continuare a essere eseguito 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 è una spazzatura completa, è stato costruito partendo dal presupposto che uno script è in esecuzione solo per pochi secondi e quindi esiste. Lo script PHP inizierà a utilizzare GIGABYTES della memoria solo dopo pochi giorni. È inoltre NECESSARIO creare uno script cron che funzioni ogni 12 o forse 24 ore per uccidere e ricreare il tuo script php in questo modo:

killall -3 php
nohup php myscript.php &

Ma cosa succede se la sceneggiatura era nel bel mezzo di un lavoro? Bene kill -3 è un interrupt, è come fare un ctrl + c sulla CLI. Lo 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 del blocco $ è che lo script PHP può aprire un file con un fopen ("file", "w") ;. Solo un processo può avere un blocco di scrittura su un file, quindi usando questo puoi essere sicuro che solo una copia del tuo script PHP è in esecuzione.

In bocca al lupo!


Corro un gran numero di demoni PHP.

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

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

Puoi verificarlo su daemontools .

EDIT: un rapido elenco di funzionalità.

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

Con il nuovo systemd puoi creare un servizio (su linux basato su rhel).

È necessario creare un file o un symlink in /etc/systemd/system/ , ad es. myphpdaemon.service e posizionare un contenuto come questo, myphpdaemon sarà il nome del servizio:

[Unit]
Description=My PHP Daemon Service
#Requires=mysqld.service memcached.service #May your script needs mysql or other services to run.
#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 a funzionare.

<?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 di lavoro:

[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
#Restart=on-failure
#RestartPreventExitStatus=1
#PrivateTmp=false
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=process

Restart=on-failure
RestartSec=42s

[Install]
WantedBy=default.target

Nota: ogni volta che cambi il tuo "myphpdaemon.service" devi eseguire `systemctl daemon-reload ', ma non preoccuparti se non lo fai, verrà richiesto quando è necessario.



Un'altra opzione è usare Upstart . È stato originariamente sviluppato per Ubuntu (e viene fornito con il pacchetto predefinito), ma è pensato per essere adatto a tutte le distribuzioni Linux.

Questo approccio è simile a Supervisord e daemontools , nel senso che avvia automaticamente il demone all'avvio del sistema e ricompare il completamento dello script.

Come configurarlo:

Crea un nuovo file di script su /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.


Puoi iniziare lo script php dalla riga di comando (es. Bash) usando

nohup php myscript.php &

il & mette il tuo processo in background.

Modificare:
Sì, ci sono alcuni inconvenienti, ma non è possibile controllarli? È semplicemente sbagliato.
Un semplice kill processid lo fermerà. Ed è ancora la soluzione migliore e più semplice.


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

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

EasyDeamonizer

Controlla semplicemente la tua applicazione (avvia, riavvia, registra, monitora, ecc.). uno script generico per assicurarti che la tua app resti funzionante correttamente. Intenzionalmente usa il nome del processo instread del file pid / lock per prevenire tutti i suoi effetti collaterali e mantenere lo script il più semplice e il più agevole possibile, quindi funziona sempre anche quando EasyDaemonizer stesso viene riavviato. Caratteristiche

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


NOTA PER PHP 7

Per aggiornare questa risposta in quanto ha acquisito una certa popolarità: questa risposta non si applica più a partire da PHP 7. Come spiegato nella sezione " Modifiche all'indietro incompatibili ", in PHP 7 foreach funziona sulla copia dell'array, quindi qualsiasi modifica sullo stesso array non si riflettono sul ciclo foreach. Maggiori dettagli al link

Spiegazione (citazione da php.net ):

Il primo modulo esegue il loop sull'array dato da array_expression. Ad ogni iterazione, il valore dell'elemento corrente viene assegnato a $ value e il puntatore dell'array interno viene avanzato di uno (quindi nella prossima iterazione, si guarderà al prossimo elemento).

Quindi, nel tuo primo esempio hai solo un elemento nell'array, e quando il puntatore viene spostato, l'elemento successivo non esiste, quindi dopo aver aggiunto il nuovo elemento foreach finisce perché già "decide" che è l'ultimo elemento.

Nel secondo esempio, si inizia con due elementi e il ciclo foreach non si trova nell'ultimo elemento, quindi valuta l'array alla successiva iterazione e quindi si rende conto che c'è un nuovo elemento nell'array.

Credo che questa sia una conseguenza di ciascuna parte della spiegazione nella documentazione, il che probabilmente significa che foreachfa tutta la logica prima di chiamare il codice {}.

Test case

Se esegui questo:

<?
    $array = Array(
        'foo' => 1,
        'bar' => 2
    );
    foreach($array as $k=>&$v) {
        $array['baz']=3;
        echo $v." ";
    }
    print_r($array);
?>

Otterrai questo risultato:

1 2 3 Array
(
    [foo] => 1
    [bar] => 2
    [baz] => 3
)

Il che significa che ha accettato la modifica e l'ha esaminata perché è stata modificata "in tempo". Ma se lo fai:

<?
    $array = Array(
        'foo' => 1,
        'bar' => 2
    );
    foreach($array as $k=>&$v) {
        if ($k=='bar') {
            $array['baz']=3;
        }
        echo $v." ";
    }
    print_r($array);
?>

Otterrete:

1 2 Array
(
    [foo] => 1
    [bar] => 2
    [baz] => 3
)

Il che significa che l'array è stato modificato, ma da quando lo abbiamo modificato quando foreachera già nell'ultimo elemento dell'array, "ha deciso" di non effettuare più il ciclo, e anche se abbiamo aggiunto un nuovo elemento, lo abbiamo aggiunto "troppo tardi" e non è stato collegato.

Una spiegazione dettagliata può essere letta in Come funziona effettivamente PHP foreach? che spiega gli interni di questo comportamento.





php linux unix daemon