crear un daemon en php
Ejecuta script php como proceso daemon (12)
Necesito ejecutar un script php como proceso daemon (esperar instrucciones y hacer cosas). El trabajo cron no lo hará por mí porque las acciones deben tomarse 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. Encontré una herramienta de libslack llamada Daemon ( http://libslack.org/daemon ) que parece ayudarme a gestionar los procesos de daemon, pero no ha habido actualizaciones en los últimos 5 años, así que me pregunto si conocen alguna otras alternativas adecuadas para mi caso. Cualquier información será realmente apreciada.
Como otros ya han mencionado, ejecutar PHP como daemon es bastante fácil, y se puede hacer usando una sola línea de comando. Pero el problema real es mantenerlo funcionando y administrarlo. He tenido el mismo problema hace bastante tiempo y aunque 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 de PHP cli. Se puede configurar como cronjob para iniciar la aplicación y contendrá la aplicación y la administrará. Si se ejecuta de nuevo, por ejemplo, a través del mismo cronjob, comprueba si la aplicación se está ejecutando o no, si lo hace, simplemente sale y deja que su instancia anterior continúe administrando la aplicación.
Lo cargué en github, no dude en usarlo: https://github.com/sinasalek/EasyDeamonizer
EasyDeamonizer
Simplemente vigila su aplicación (inicio, reinicio, registro, monitor, etc.). una secuencia de comandos genérica para asegurarse de que su aplicación siga funcionando 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 sofisticado posible, por lo que siempre funciona incluso cuando EasyDaemonizer se reinicia. Caracteristicas
- Inicia la aplicación y, opcionalmente, un retraso personalizado para cada inicio
- Asegura 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
Con el nuevo systemd puede crear un servicio (en rhel basado en Linux).
Debe crear un archivo o un symlink en /etc/systemd/system/
, ej. myphpdaemon.service y coloque un contenido como este, myphpdaemon será el nombre del servicio:
[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
Podrá iniciar, obtener el estado, reiniciar y detener los servicios utilizando el comando
systemctl <start|status|restart|stop|enable> myphpdaemon
El script PHP debe tener un tipo de "bucle" para continuar ejecutando.
<?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
#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: Cada vez que cambie su "myphpdaemon.service" debe ejecutar `systemctl daemon-reload '', pero si no lo hace, pregúntese cuando sea necesario.
Consulte https://github.com/shaneharter/PHP-Daemon
Esta es una biblioteca daemon 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.
Ejecuto una gran cantidad de daemons de PHP.
Estoy de acuerdo con usted en que PHP no es el mejor (ni siquiera un buen) lenguaje para hacer esto, pero los daemons comparten el código con los componentes que se encuentran en la web, por lo que, en general, es una buena solución para nosotros.
Usamos daemontools para esto. Es inteligente, limpio y confiable. De hecho, lo usamos para ejecutar todos nuestros daemons.
Puede verificar esto en daemontools .
EDITAR: una lista rápida de características.
- Inicia automáticamente el daemon al reiniciar
- Reiniciar automáticamente dameon en caso de falla
- El registro se maneja por usted, incluido el vuelco y la poda
- Interfaz de gestión: ''svc'' y ''svstat''
- UNIX amigable (no un plus para todos quizás)
Escribí e implementé un php-daemon simple, el código está en línea aquí
https://github.com/jmullee/PhpUnixDaemon
Características: caída de privilegios, manejo de señal, registro
Lo usé en un manejador de colas (caso de uso: desencadenar una operación prolongada desde una página web, sin hacer esperar a la generación de páginas php, es decir, iniciar una operación asincrónica) https://github.com/jmullee/PhpIPCMessageQueue
Hay más de una forma de resolver este problema.
No conozco los detalles, pero tal vez haya otra forma de activar el proceso de PHP. Por ejemplo, si necesita que el código se ejecute en función de los eventos en una base de datos SQL, puede configurar un disparador para ejecutar su secuencia de comandos. 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 la sesión:
nohup php myscript.php &
Sin embargo, hay un problema muy serio. Como dijo que 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 mata y vuelve 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, así que al usarlo puede asegurarse de que solo se esté ejecutando una copia de su script PHP.
¡Buena suerte!
Kevin van Zonneveld escribió un muy buen artículo detallado sobre esto , en su ejemplo utiliza el paquete System_Daemon
PEAR (última fecha de lanzamiento el 2009-09-02).
Otra opción es usar Upstart . Originalmente fue desarrollado para Ubuntu (y viene empaquetado con él por defecto), pero está destinado a ser adecuado para todas las distribuciones de Linux.
Este enfoque es similar a Supervisord y daemontools , ya que inicia automáticamente el daemon en el arranque del sistema y reaparece en la finalización del 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
Iniciando y deteniendo a tu daemon:
sudo service myphpworker start
sudo service myphpworker stop
Verifica si tu daemon se está ejecutando:
sudo service myphpworker status
Gracias
Muchas gracias a Kevin van Zonneveld , de donde aprendí esta técnica.
Puedes comenzar tu script php desde la línea de comando (es decir, bash) usando
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 processid
lo detendrá. Y sigue siendo la mejor y más simple solución.
Recientemente tuve la necesidad de una solución multiplataforma (Windows, Mac y Linux) para el problema de ejecutar scripts PHP como daemons. Resolví el problema escribiendo mi propia solución basada en C ++ y haciendo 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.
Si solo necesita Linux, entonces algunas de las otras soluciones presentadas aquí funcionan bastante bien y, dependiendo del sabor. También hay Upstart y systemd en estos días, que tienen inconvenientes con sysvinit scripts. Pero la mitad del punto de utilizar PHP es que es de naturaleza multiplataforma, por lo que el código escrito en el idioma tiene una gran 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 scripting.
Intentar captar señales como alguien sugirió aquí en PHP userland no es una buena idea. Lea la documentación en pcntl_signal()
cuidado y rápidamente aprenderá que PHP maneja las señales usando algunos métodos bastante desagradables (específicamente, ''tics'') que mastican un montón de ciclos por algo que los procesos raramente ven (es decir, señales). El manejo de señales en PHP apenas está disponible en 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 sus problemas de pérdida de memoria a medida que pasa el tiempo. Aún debe tener cuidado (el analizador DOM XML tiende a filtrarse) pero rara vez veo procesos fuera de control en estos días y el rastreador de fallos de PHP es bastante silencioso en comparación con los días de antaño.
Si puede, obtenga una copia de Programación avanzada en el entorno UNIX . Todo el capítulo 13 está dedicado a la programación daemon. Los ejemplos están en C, pero todas las funciones que necesita tienen envoltorios en PHP (básicamente las extensiones pcntl y posix ).
En pocas palabras, escribir un daemon (esto es posible solo en OS-es * basados en * nix - Windows usa servicios) es como esto:
- Llame a
umask(0)
para evitar problemas de permisos. -
fork()
y hacer que el padre salga. - Llamar
setsid()
. - Configure el procesamiento de la señal de
SIGHUP
(generalmente esto se ignora o se usa para indicar al daemon que vuelva a cargar su configuración) ySIGTERM
(para indicarle al proceso que salga con elegancia). -
fork()
nuevamente y haga que el padre salga. - Cambia el directorio de trabajo actual con
chdir()
. -
fclose()
stdin
,stdout
ystderr
y no les escribas. La forma correcta es redirigirlos a/dev/null
o a un archivo, pero no pude encontrar la forma de hacerlo en PHP. Es posible cuando ejecutas el daemon para redirigirlos usando el shell (tendrás que averiguar cómo hacerlo, no sé :). - ¡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.
Usted puede
- Use
nohup
como sugirió Henrik. - Usa la
screen
y ejecuta tu programa PHP como un proceso regular dentro de eso. Esto le da más control que usarnohup
. - Utilice un daemonizador como Supervisord (está escrito en Python pero puede demonizar cualquier programa de línea de comandos y darle un control remoto para administrarlo).
- Escribe tu propia envoltura daemonise como sugirió Emil, pero es IMO excesivo.
Yo recomendaría el método más simple (en mi opinión, la pantalla) y luego, si quieres más características o funcionalidades, pasa a métodos más complejos.