example php gearman

php - example - Deteniendo a los trabajadores de Gearman amablemente



gearman php example (12)

Solución 1

En general, ejecuto a mis trabajadores con la utilidad daemon de Unix con el distintivo -r y los dejo caducar después de un trabajo. La secuencia de comandos finalizará correctamente después de cada iteración y el daemon se reiniciará automáticamente.

Sus trabajadores estarán obsoletos para un trabajo, pero eso puede no ser tan importante para usted como perder datos

Esta solución también tiene la ventaja de liberar memoria. Puede tener problemas con la memoria si está realizando grandes trabajos, ya que PHP pre 5.3 tiene un tremendo GC.

Solución 2

También podría agregar una función para dejar de fumar a todos sus trabajadores que salga de la secuencia de comandos. Cuando desee reiniciar, simplemente debe llamar a Gearman para que deje de fumar con una alta prioridad.

Tengo un número de trabajadores de Gearman ejecutándose constantemente, guardando cosas como registros de visitas a páginas de usuario, etc. Ocasionalmente, actualizaré el código PHP que usan los trabajadores de Gearman. Para que los trabajadores cambien al nuevo código, elimino y reinicio los procesos de PHP para los trabajadores.

¿Cuál es una mejor manera de hacer esto? Presumiblemente, en algún momento estoy perdiendo datos (aunque no sean datos muy importantes) cuando elimino uno de esos procesos de trabajo.

Editar: Encontré una respuesta que me funciona y la publiqué a continuación.


Bueno, publiqué esta pregunta, ahora creo que encontré una buena respuesta.

Si busca en el código para Net_Gearman_Worker, encontrará que en el ciclo de trabajo, la función stopWork es monitoreada, y si devuelve verdadero, sale de la función.

Hice lo siguiente:
Utilizando Memcache, creé un valor en caché, gearman_restarttime, y uso un script separado para configurarlo en la marca de tiempo actual cada vez que actualizo el sitio. (Utilicé Memcache, pero esto podría almacenarse en cualquier lugar: una base de datos, un archivo o cualquier otra cosa).

Extendí la clase Worker para que sea, en esencia, Net_Gearman_Worker_Foo, e hice que todos mis trabajadores crearan una instancia. En la clase Foo, superé la función stopWork para hacer lo siguiente: primero, comprueba gearman_restarttime; la primera vez, guarda el valor en una variable global. A partir de ese momento, cada vez que lo hace, compara el valor en caché con el global. Si ha cambiado, el stopWork devuelve verdadero, y el trabajador se cierra. Un cron revisa cada minuto para ver si cada trabajador todavía se está ejecutando, y reinicia a cualquier trabajador que se haya marchado.

También puede valer la pena poner un temporizador en stopWork y verificar la caché solo una vez cada x minutos. En nuestro caso, Memcache es lo suficientemente rápido como para comprobar que el valor cada vez no parece ser un problema, pero si está utilizando algún otro sistema para almacenar la marca de tiempo actual, sería menos recomendable verificarlo con menos frecuencia.


Dado que los trabajadores están escritos en PHP, sería una buena idea reciclarlos en un horario conocido. Esta puede ser una cantidad de tiempo estática desde que se inició o puede realizarse después de haber intentado una cierta cantidad de trabajos.

Esto esencialmente mata (sin juego de palabras) dos pájaros de un tiro. Usted está mitigando el potencial de pérdidas de memoria, y tiene una manera consistente de determinar cuándo sus trabajadores detectarán cualquier código potencialmente nuevo.

En general, escribo a los trabajadores de modo que informen sobre su intervalo para la extensión estándar y / o para una instalación de registro, por lo que es fácil verificar dónde se encuentra el trabajador en el proceso.


Esto encajaría perfectamente en su sistema de integración continua. Espero que lo tengas o deberías tenerlo pronto :-)

A medida que ingresa el código nuevo, automáticamente se genera e implementa en el servidor. Como parte del script de construcción, matas a todos los trabajadores y lanzas otros nuevos.


Hmm, podrías implementar un código en los trabajadores para verificar ocasionalmente si se modificó el código fuente; si es así, simplemente mátelos a ti mismo cuando lo consideren oportuno. Es decir, verifique si están en el medio del trabajo y si el trabajo es muy grande.

De otra manera sería implementar algún tipo de interrupción, tal vez a través de la red para decir detener cada vez que tenga la oportunidad y reiniciar.

La última solución está ayudando a modificar la fuente de Gearman para incluir esta funcionalidad.


Lo que hago es usar gearmadmin para verificar si hay trabajos en ejecución. Usé la API de administrador para hacer una interfaz de usuario para esto. Cuando los trabajos están sentados sin hacer nada, no hay daño en matarlos.


Me encontré con este mismo problema y se me ocurrió una solución para python 2.7.

Estoy escribiendo un script de Python que usa Gearman para comunicarse con otros componentes en el sistema. El script tendrá varios trabajadores y cada trabajador se ejecutará en un hilo separado. Todos los trabajadores reciben datos de engranaje, procesan y almacenan esos datos en una cola de mensajes, y el hilo principal puede sacar los datos de la cola según sea necesario.

Mi solución para cerrar a cada trabajador fue subclass gearman.GearmanWorker y anular la función work() :

from gearman import GearmanWorker POLL_TIMEOUT_IN_SECONDS = 60.0 class StoppableWorker(GearmanWorker): def __init__(self, host_list=None): super(StoppableWorker,self).__init__(host_list=host_list) self._exit_runloop = False # OVERRIDDEN def work(self, poll_timeout=POLL_TIMEOUT_IN_SECONDS): worker_connections = [] continue_working = True def continue_while_connections_alive(any_activity): return self.after_poll(any_activity) while continue_working and not self._exit_runloop: worker_connections = self.establish_worker_connections() continue_working = self.poll_connections_until_stopped( worker_connections, continue_while_connections_alive, timeout=poll_timeout) for current_connection in worker_connections: current_connection.close() self.shutdown() def stopwork(self): self._exit_runloop = True

Úselo como GearmanWorker. Cuando sea el momento de salir del script, llame a la función stopwork() . No se detendrá inmediatamente, puede tomar hasta poll_timeout segundos antes de que salga del ciclo de ejecución.

Puede haber varias formas inteligentes de invocar la función de stopwork() . En mi caso, creo un cliente de engranaje temporal en el hilo principal. Para el trabajador que estoy tratando de cerrar, envío un comando especial STOP a través del servidor de Gearman. Cuando el trabajador recibe este mensaje, sabe que se apaga solo.

¡Espero que esto ayude!


Si alguien estaba buscando una respuesta para un trabajador que ejecuta perl, eso es parte de lo que es la biblioteca GearmanX::Starter . Puede detener a los trabajadores después de completar el trabajo actual de dos maneras diferentes: externamente enviando un proceso de trabajo al SIGTERM, o programáticamente estableciendo una variable global.


También he estado viendo esto recientemente (aunque en perl con Gearman :: XS). Mi caso de uso era el mismo que el tuyo: permite a un trabajador de engranaje de larga duración verificar periódicamente una nueva versión de sí mismo y volver a cargar.

Mi primer intento fue hacer que el trabajador realizara un seguimiento del tiempo transcurrido desde la última vez que verificó la versión del script del trabajador (un md5sum también funcionaría). Luego, una vez transcurridos N segundos, entre trabajos, verificaría si una nueva versión de sí mismo estaba disponible y se reiniciaría (fork () / exec ()). Esto funcionó bien, pero los trabajadores registrados para trabajos poco comunes podrían terminar esperando horas de trabajo () para regresar y, por lo tanto, para verificar la hora actual.

Así que ahora estoy estableciendo un tiempo de espera bastante corto cuando espero trabajos con el trabajo (), así puedo verificar el tiempo más regularmente. La interfaz de PHP sugiere que puede establecer este valor de tiempo de espera al registrarse para el trabajo. Estoy usando SIGALRM para activar la verificación de nueva versión. La interfaz perl bloquea el trabajo (), por lo que la alarma no se activaba inicialmente. Al establecer el tiempo de espera en 60 segundos, el SIGALRM funcionó.


Utilizo el siguiente código que admite tanto Ctrl-C como kill -TERM . Por defecto, el supervisor envía la señal TERM si no ha modificado signal= setting. En PHP 5.3+ declare(ticks = 1) está en desuso, use pcntl_signal_dispatch() lugar.

$terminate = false; pcntl_signal(SIGINT, function() use (&$terminate) { $terminate = true; }); pcntl_signal(SIGTERM, function() use (&$terminate) { $terminate = true; }); $worker = new GearmanWorker(); $worker->addOptions(GEARMAN_WORKER_NON_BLOCKING); $worker->setTimeout(1000); $worker->addServer(''127.0.0.1'', 4730); $worker->addFunction(''reverse'', function(GearmanJob $job) { return strrev($job->workload()); }); $count = 500 + rand(0, 100); // rand to prevent multple workers restart at same time for($i = 0; $i < $count; $i++) { if ( $terminate ) { break; } else { pcntl_signal_dispatch(); } $worker->work(); if ( $terminate ) { break; } else { pcntl_signal_dispatch(); } if ( GEARMAN_SUCCESS == $worker->returnCode() ) { continue; } if ( GEARMAN_IO_WAIT != $worker->returnCode() && GEARMAN_NO_JOBS != $worker->returnCode() ) { $e = new ErrorException($worker->error(), $worker->returnCode()); // log exception break; } $worker->wait(); } $worker->unregisterAll();



function AutoRestart() { static $startTime = time(); if (filemtime(__FILE__) > $startTime) { exit(); } } AutoRestart();