perl daemon

Perl daemonize con demonios infantiles



(1)

Daemon es solo una palabra clave para "proceso en segundo plano que dura mucho tiempo". Entonces la respuesta es ''depende''. Perl tiene dos formas principales de hacer multiprocesamiento:

Enhebrado

Ejecuta una subrutina como un hilo, en paralelo con el código del programa principal. (Que luego puede simplemente monitorear estados de hilos).

La sobrecarga de crear un hilo es más alta, pero es más adecuada para el multiprocesamiento de estilo de "memoria compartida", por ejemplo, cuando transfiere cantidades significativas de datos hacia adelante y hacia atrás. Hay varias bibliotecas que hacen que pasar información entre hilos sea positivamente sencillo. Personalmente, me gusta bastante Thread::Queue , Thread::Semaphore y Storable .

En particular, Storable tiene freeze y thaw que le permite mover complejas estructuras de datos (por ejemplo, objetos / hash) en colas, lo cual es muy útil.

Ejemplo de subprocesamiento básico:

#!/usr/bin/perl use strict; use warnings; use threads; use Thread::Queue; my $nthreads = 5; my $process_q = Thread::Queue->new(); my $failed_q = Thread::Queue->new(); #this is a subroutine, but that runs ''as a thread''. #when it starts, it inherits the program state ''as is''. E.g. #the variable declarations above all apply - but changes to #values within the program are ''thread local'' unless the #variable is defined as ''shared''. #Behind the scenes - Thread::Queue are ''shared'' arrays. sub worker { #NB - this will sit a loop indefinitely, until you close the queue. #using $process_q -> end #we do this once we''ve queued all the things we want to process #and the sub completes and exits neatly. #however if you _don''t_ end it, this will sit waiting forever. while ( my $server = $process_q->dequeue() ) { chomp($server); print threads->self()->tid() . ": pinging $server/n"; my $result = `/bin/ping -c 1 $server`; if ($?) { $failed_q->enqueue($server) } print $result; } } #insert tasks into thread queue. open( my $input_fh, "<", "server_list" ) or die $!; $process_q->enqueue(<$input_fh>); close($input_fh); #we ''end'' process_q - when we do, no more items may be inserted, #and ''dequeue'' returns ''undefined'' when the queue is emptied. #this means our worker threads (in their ''while'' loop) will then exit. $process_q->end(); #start some threads for ( 1 .. $nthreads ) { threads->create( /&worker ); } #Wait for threads to all finish processing. foreach my $thr ( threads->list() ) { $thr->join(); } #collate results. (''synchronise'' operation) while ( my $server = $failed_q->dequeue_nb() ) { print "$server failed to ping/n"; }

Almacenable

Cuando se trata de Storable, creo que vale la pena un ejemplo separado, porque es útil para mover datos.

use Storable qw ( freeze thaw ); use MyObject; #home made object. use Thread::Queue; my $work_q = Thread::Queue->new(); sub worker_thread { while ( my $packed_item = $work_q->dequeue ) { my $object = thaw($packed_item); $object->run_some_methods(); $object->set_status("processed"); #maybe return $object via ''freeze'' and a queue? } } my $thr = threads->create( /&worker_thread ); my $newobject = MyObject->new("some_parameters"); $work_q->enqueue( freeze($newobject) ); $work_q->end(); $thr->join();

Debido a que está pasando el objeto dentro de la cola, lo clonará efectivamente entre hilos. Así que tenga en cuenta que puede necesitar congelarlo y ''devolverlo'' de alguna manera una vez que haya hecho algo en su estado interno. Pero sí significa que puede hacerlo de forma asincrónica sin necesidad de arbitrar el bloqueo o la memoria compartida. También puede resultarle útil poder "almacenar", "recuperar" y objetar; esto funciona como podría esperar. (Aunque me atrevo a decir que es posible que deba tener cuidado con la disponibilidad de las versiones del módulo frente a los atributos definidos si está recuperando un objeto almacenado)

Bifurcación

Su script se clona a sí mismo, dejando un ''padre'' y ''hijo'' - el niño generalmente diverge y hace algo diferente. Utiliza la fork() incorporada de Unix fork() que, como resultado, está bien optimizada y generalmente es muy eficiente, pero debido a su bajo nivel, significa que es difícil hacer una gran cantidad de transferencia de datos. Terminará algunas cosas ligeramente complicadas para hacer Comunicación entre procesos - IPC. (Ver perlipc para más detalles). Es eficiente porque la mayoría de las implementaciones de fork() hacen una copia de datos perezosa: el espacio de memoria para su proceso solo se asigna como es necesario, por ejemplo, cuando se cambia.

Por lo tanto, es realmente bueno si desea delegar muchas tareas que no requieren mucha supervisión del padre. Por ejemplo, puede fork un servidor web, porque el niño está leyendo archivos y entregándoselos a un cliente en particular, y al padre no le importa demasiado. O quizás lo haría si desea gastar una gran cantidad de tiempo de CPU calculando un resultado, y solo devolver ese resultado.

Tampoco es compatible con Windows.

Las bibliotecas útiles incluyen Parallel::ForkManager

Un ejemplo básico del código ''bifurcación'' se parece un poco a esto:

#!/usr/bin/perl use strict; use warnings; use Parallel::ForkManager; my $concurrent_fork_limit = 4; my $fork_manager = Parallel::ForkManager->new($concurrent_fork_limit); foreach my $thing ( "fork", "spoon", "knife", "plate" ) { my $pid = $fork_manager->start; if ($pid) { print "$$: Fork made a child with pid $pid/n"; } else { print "$$: child process started, with a key of $thing ($pid)/n"; } $fork_manager->finish; } $fork_manager->wait_all_children();

¿Cuál es el adecuado para usted?

Entonces es difícil decir sin más detalles sobre lo que estás tratando de lograr. Esta es la razón por la cual a StacKOverflow por lo general le gusta mostrar algunos métodos de trabajo, enfoques que ha probado, etc.

Yo generalmente diría:

  • si necesita pasar datos, use hilos. Thread::Queue especialmente cuando se combina con Storable es muy bueno para eso.

  • si no lo hace, los tenedores (en Unix) generalmente son más rápidos / más eficientes. (Pero lo rápido por sí solo no suele ser suficiente, primero escribe cosas comprensibles y apúntate a la velocidad en segundo lugar. No importa mucho por lo general).

Evite, en lo posible, engendrar demasiados hilos: son bastante intensivos en la memoria y la creación. Es mucho mejor utilizar un número fijo en un estilo de programación de "hilo de trabajo" que crear repetidamente hilos nuevos y de corta duración. (Por otro lado, los tenedores son muy buenos en esto, porque no copian todo el proceso).

Sugeriría en el escenario que das, estás viendo los hilos y las colas. El proceso principal puede rastrear hilos secundarios a través de threads -> list() y join o create para mantener el número correcto. Y puede enviar datos a través de una cola central a sus hilos de trabajo. O tiene varias colas, una por ''hijo'' y la usa como un sistema de asignación de tareas.

Tengo que emplear daemons en mi código. Necesito un daemon de control que compruebe constantemente la base de datos para las tareas y supervise los demonios secundarios. El demonio de control debe asignar tareas a los demonios secundarios, controlar tareas, crear nuevos hijos si uno de ellos muere, etc. Los daemons secundarios verifican las tareas de la base de datos (por PID). ¿Cómo debería implementar daemons para este propósito?