Ejecute el script php como proceso daemon

154

Necesito ejecutar un script php como proceso daemon (esperar instrucciones y hacer cosas). cron job no lo hará por mí porque se deben tomar medidas tan pronto como llegue la instrucción. Sé que PHP no es realmente la mejor opción para los procesos de daemon debido a problemas de administración de memoria, pero debido a varias razones, tengo que usar PHP en este caso. Me encontré con una herramienta de libslack llamada Daemon ( http://libslack.org/daemon ) que parece ayudarme a administrar los procesos de daemon, pero no ha habido actualizaciones en los últimos 5 años, así que me pregunto si conoces algunas Otras alternativas adecuadas para mi caso. Cualquier información será realmente apreciada.

Beier
fuente
2
Por favor revisa mi respuesta. gracias
Henrik P. Hessel
1
Me encontré con esta publicación gonzalo123.com/2010/05/23/… que creo que es a la vez relajante y estable.
Teson
Es muy fácil hacerlo con systemd
LeonanCarvalho

Respuestas:

167

Puede iniciar su script php desde la línea de comando (es decir, bash) utilizando

nohup php myscript.php &

el &pone su proceso en segundo plano.

Editar:
Sí, hay algunos inconvenientes, pero ¿no es posible controlarlos? Eso está mal.
Un simple kill processidlo detendrá. Y sigue siendo la mejor y más simple solución.

Henrik P. Hessel
fuente
Si el terminal existe, el proceso NO se cerrará. Es por eso que el comando "nohup" está ahí. He estado usando un script PHP como daemon en todos los servidores como este durante años. Puede haber una mejor solución, pero esta es la más rápida.
CDR
27
Esto no reiniciará el demonio si falla, y no hay una manera fácil de administrarlo.
Phil Wallach
66
Estoy de acuerdo con lo que se ha dicho aquí: esta es una mala solución. Debe crear una secuencia de comandos de inicio por un par de razones: 1) La secuencia de comandos de inicio se inicia automáticamente al inicio 2) Puede administrar el demonio con los comandos de inicio / detención / reinicio. Aquí hay un ejemplo de servefault: serverfault.com/questions/229759/…
Simian
1
Hola chicos ... me parece nohupy &hace lo mismo: desconectando el proceso iniciado de la actual integridad del shell. ¿Por qué los necesito a ambos? ¿No puedo simplemente hacer php myscript.php &o nohup myscript.php?? Gracias
nourdine
1
Si el script escribe en stdout (a través de echo o var_dump), puede capturar esta información con un archivo de registro como este:nohup php myscript.php > myscript.log &
Mischa
167

Otra opción es usar Upstart . Fue desarrollado originalmente para Ubuntu (y viene empaquetado con él de manera predeterminada), pero está diseñado para ser adecuado para todas las distribuciones de Linux.

Este enfoque es similar a Supervisord y daemontools , ya que automáticamente inicia el demonio al arrancar el sistema y reaparece al completar el script.

Cómo configurarlo:

Cree un nuevo archivo de script en /etc/init/myphpworker.conf. Aquí hay un ejemplo:

# 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

Iniciar y detener tu demonio:

sudo service myphpworker start
sudo service myphpworker stop

Comprueba si tu demonio se está ejecutando:

sudo service myphpworker status

Gracias

Muchas gracias a Kevin van Zonneveld , de donde aprendí esta técnica.

Jonathan
fuente
2
Amando esto. Solo me pregunto, ¿es posible tener múltiples trabajadores concurrentes? Solo tengo el problema de que un trabajador ya no es suficiente.
Manuel
1
¿Esto se ejecutará automáticamente al iniciar el sistema?
Slier
2
Sudo "servicio myphpworker start" no funcionó para mí. Usé "sudo start myphpworker" y funciona perfectamente
Matt Sich
3
@Pradeepta Eso se debe a que hay un error en la publicación: no estoy exactamente seguro de qué (y no lo he probado), pero creo que sudo service myphpworker start/stop/statussolo funciona con servicios que no se encuentran en /etc/init.dservicios nuevos. @ matt-sich parece haber descubierto la sintaxis correcta. Otra opción es usar Gearman o Resque, que permite el procesamiento en segundo plano y la desamonización.
ckm
3
Ubuntu se está moviendo a usar systemd en lugar de upstart: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai
72

Con nuevo systemd puedes crear un servicio.

Debe crear un archivo o un enlace simbólico en /etc/systemd/system/, por ejemplo. myphpdaemon.service y colocar contenido como este, myphpdaemon será el nombre del servicio:

[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

Podrá iniciar, obtener el estado, reiniciar y detener los servicios con el comando

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

El script PHP debería tener una especie de "bucle" para continuar ejecutándose.

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

Ejemplo de trabajo:

[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 su rutina PHP debe ejecutarse una vez en un ciclo (como un diggest), puede usar un script de shell o bash para invocarlo en el archivo de servicio systemd en lugar de PHP directamente, por ejemplo:

#!/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 elige esta opción, debe cambiar KillMode a mixedprocesos, bash (main) y PHP (child) se eliminarán.

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: Cada vez que cambie su "myphpdaemon.service" debe ejecutar `systemctl daemon-reload ', pero no se preocupe si no lo hace, se le avisará cuando sea necesario.

LeonanCarvalho
fuente
77
Respuesta infravalorada. Tienes mi +1.
Gergely Lukacsy
2
Increíble. Ojalá pudiéramos responder también porque esto no debería estar enterrado en esta página.
Justin
1
Debe verificar la systemctl status <your_service_name> -lsalida, le dará una pista de lo que está sucediendo.
LeonanCarvalho
1
@LeandroTupone MySQL y Memcached fue una demostración de cómo usar las dependencias del servicio, no es necesario.
LeonanCarvalho
3
Esto realmente debería reemplazar la respuesta aceptada, ya que ahora es 2019.
especia
47

Si puede, tome una copia de la Programación avanzada en el entorno UNIX . Todo el capítulo 13 está dedicado a la programación de demonios. Los ejemplos están en C, pero todas las funciones que necesita tienen envoltorios en PHP (básicamente pcntl y posix extensiones ).

En pocas palabras, escribir un demonio (esto solo es posible en sistemas operativos basados ​​en * nix - Windows usa servicios) es así:

  1. Llame umask(0)para evitar problemas de permisos.
  2. fork() y que el padre salga.
  3. Llamada setsid() .
  4. Configurar el procesamiento de la señal de SIGHUP(generalmente esto se ignora o se usa para indicar al demonio que vuelva a cargar su configuración) ySIGTERM para indicarle al (para indicarle al proceso que salga con gracia).
  5. fork() de nuevo y que el padre salga.
  6. Cambie el directorio de trabajo actual con chdir().
  7. fclose() stdin, stdoutY stderry no escribir en ellos. La forma correcta es redirigirlos a/dev/null o a un archivo, pero no pude encontrar una manera de hacerlo en PHP. Es posible cuando ejecutas el demonio para redirigirlos usando el shell (tendrás que descubrir cómo hacerlo, no lo sé :).
  8. ¡Haz tu trabajo!

Además, dado que está utilizando PHP, tenga cuidado con las referencias cíclicas, ya que el recolector de basura PHP, antes de PHP 5.3, no tiene forma de recopilar esas referencias y el proceso perderá memoria, hasta que finalmente se bloquee.

Emil Ivanov
fuente
1
Gracias por la info. Parece que el programa demonio de libslack hace todo el trabajo de preparación como mencionaste. Creo que por ahora me quedaré con él hasta que encuentre otras buenas alternativas.
Beier el
1
Encontré esta publicación, el código esperado para copiar y pegar en una aplicación vieja que no puede cerrar stdin, etc., fue decepcionado. : p
ThiefMaster
1
¿Por qué (5) tenedor () de nuevo?
TheFox
Trabajó para mí, ¡excelente trabajo!
Gautam Sharma
Para futuros lectores que preguntan por qué bifurcar dos veces: stackoverflow.com/questions/881388/… - TL; DR: Previene zombies.
Ghedipunk
24

Ejecuto una gran cantidad de demonios PHP.

Estoy de acuerdo con usted en que PHP no es el mejor (o incluso un buen) lenguaje para hacer esto, pero los demonios comparten código con los componentes orientados a la web, por lo que, en general, es una buena solución para nosotros.

Utilizamos daemontools para esto. Es inteligente, limpio y confiable. De hecho, lo usamos para ejecutar todos nuestros demonios.

Puede consultar esto en http://cr.yp.to/daemontools.html .

EDITAR: una lista rápida de características.

  • Inicia automáticamente el demonio al reiniciar
  • Reiniciar automáticamente Dameon en caso de falla
  • El registro se gestiona por usted, incluidas las transferencias y la poda.
  • Interfaz de gestión: 'svc' y 'svstat'
  • UNIX amigable (quizás no sea una ventaja para todos)
Phil Wallach
fuente
También se puede instalar desde los repositorios, por ejemplo, en apt!
Kzqai
14

Usted puede

  1. Úselo nohupcomo Henrik sugirió.
  2. Use screeny ejecute su programa PHP como un proceso regular dentro de eso. Esto te da más control que usar nohup.
  3. Use un demonio como http://supervisord.org/ (está escrito en Python pero puede demonizar cualquier programa de línea de comandos y darle un control remoto para administrarlo).
  4. Escribe tu propio envoltorio de daemonise como sugirió Emil, pero es excesivo IMO.

Recomendaría el método más simple (pantalla en mi opinión) y luego, si desea más características o funcionalidad, vaya a métodos más complejos.

Noufal Ibrahim
fuente
¿Podría proporcionar una configuración de supervisor similar?
Alix Axel
11

Hay más de una forma de resolver este problema.

No conozco los detalles, pero quizás haya otra forma de activar el proceso PHP. Por ejemplo, si necesita que el código se ejecute en función de eventos en una base de datos SQL, puede configurar un disparador para ejecutar su script. Esto es realmente fácil de hacer en PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html .

Honestamente, creo que tu mejor opción es crear un proceso Damon usando nohup. nohup permite que el comando continúe ejecutándose incluso después de que el usuario haya cerrado sesión:

nohup php myscript.php &

Sin embargo, hay un problema muy grave. Como dijiste, el administrador de memoria de PHP es una basura completa, se construyó con la suposición de que un script solo se ejecuta durante unos segundos y luego existe. Su script PHP comenzará a usar GIGABYTES de memoria después de solo unos días. TAMBIÉN DEBE crear un script cron que se ejecute cada 12 o tal vez 24 horas que mate y vuelva a generar su script php de esta manera:

killall -3 php
nohup php myscript.php &

Pero, ¿y si el guión estuviera en el medio de un trabajo? Bueno, kill -3 es una interrupción, es lo mismo que hacer un ctrl + c en la CLI. Su script php puede detectar esta interrupción y salir con gracia utilizando la biblioteca PHP pcntl: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Aquí hay un ejemplo:

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

La idea detrás del $ lock es que el script PHP puede abrir un archivo con un fopen ("archivo", "w") ;. Solo un proceso puede tener un bloqueo de escritura en un archivo, por lo que puede asegurarse de que solo se esté ejecutando una copia de su script PHP.

¡Buena suerte!

torre
fuente
6

Echa un vistazo a https://github.com/shaneharter/PHP-Daemon

Esta es una biblioteca de demonios orientada a objetos. Tiene soporte incorporado para cosas como el registro y la recuperación de errores, y tiene soporte para crear trabajadores en segundo plano.

Shane H
fuente
3

Recientemente tuve la necesidad de una solución multiplataforma (Windows, Mac y Linux) para el problema de ejecutar scripts PHP como demonios. Resolví el problema escribiendo mi propia solución basada en C ++ y creando binarios:

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

Soporte completo para Linux (a través de sysvinit), pero también servicios de Windows NT y Mac OSX lanzados.

Si solo necesita Linux, entonces algunas de las otras soluciones presentadas aquí funcionan lo suficientemente bien y, dependiendo del sabor. También hay Upstart y systemd en estos días, que tienen retrocesos para sysvinit scripts. Pero la mitad del punto de usar PHP es que es de naturaleza multiplataforma, por lo que el código escrito en el lenguaje tiene una muy buena posibilidad de funcionar en todas partes tal como está. Las deficiencias comienzan a aparecer cuando ciertos aspectos externos del nivel del sistema operativo nativo entran en escena, como los servicios del sistema, pero obtendrá ese problema con la mayoría de los lenguajes de secuencias de comandos.

Intentar captar señales como alguien aquí sugirió en PHP userland no es una buena idea. Lea la documentación pcntl_signal()detenidamente y aprenderá rápidamente que PHP maneja señales usando algunos métodos bastante desagradables (específicamente, 'ticks') que mastican un montón de ciclos para algo raramente visto por los procesos (es decir, señales). El manejo de señales en PHP también está apenas disponible en las plataformas POSIX y el soporte difiere según la versión de PHP. Inicialmente suena como una solución decente, pero no llega a ser realmente útil.

PHP también ha mejorado con respecto a los problemas de pérdida de memoria a medida que pasa el tiempo. Todavía debe tener cuidado (el analizador DOM XML tiende a filtrarse aún), pero en estos días rara vez veo procesos fuera de control y el rastreador de errores de PHP es bastante silencioso en comparación con los días anteriores.

CubículoSoft
fuente
1

Como otros ya han mencionado, ejecutar PHP como demonio es bastante fácil y se puede hacer usando una sola línea de comando. Pero el problema real es mantenerlo en funcionamiento y administrarlo. He tenido el mismo problema hace bastante tiempo y, aunque ya hay muchas soluciones disponibles, la mayoría de ellas tienen muchas dependencias o son difíciles de usar y no son adecuadas para usos básicos. Escribí un script de shell que puede administrar cualquier proceso / aplicación, incluidos los scripts PHP cli. Se puede configurar como cronjob para iniciar la aplicación y contendrá la aplicación y la administrará. Si se ejecuta nuevamente, por ejemplo a través del mismo cronjob, verifica si la aplicación se está ejecutando o no, si lo hace, simplemente se cierra y deja que su instancia anterior continúe administrando la aplicación.

Lo cargué en github, siéntase libre de usarlo: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Simplemente vigila su aplicación (inicio, reinicio, registro, monitor, etc.). un script genérico para asegurarse de que su aplicación siga ejecutándose correctamente. Intencionalmente utiliza el nombre de proceso instread del archivo pid / lock para evitar todos sus efectos secundarios y mantener el script lo más simple y dinámico posible, por lo que siempre funciona incluso cuando se reinicia EasyDaemonizer. Caracteristicas

  • Inicia la aplicación y, opcionalmente, un retraso personalizado para cada inicio
  • Se asegura de que solo se esté ejecutando una instancia
  • Supervisa el uso de la CPU y reinicia la aplicación automáticamente cuando alcanza el umbral definido
  • Configurar EasyDeamonizer para que se ejecute a través de cron para ejecutarlo nuevamente si se detiene por algún motivo
  • Registra su actividad
Sina Salek
fuente
1

Ampliando la respuesta de Emil Ivaov , puede hacer lo siguiente para cerrar STDIN, STDOUT AND 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');

Básicamente, cierra las transmisiones estándar para que PHP no tenga lugar para escribir. Las siguientes fopenllamadas establecerán el IO estándar en /dev/null.

He leído esto del libro de Rob Aley - PHP más allá de la web

Raheel
fuente
0

puedes consultar pm2 aquí, http://pm2.keymetrics.io/

cree un archivo ssh, como worker.sh, que se incluirá en su script php con el que tratará.

trabajador.sh

php /path/myscript.php

comienzo del demonio

pm2 start worker.sh

Saludos, eso es todo.

Serkan Koch
fuente