Perl demonize с дочерними демонами

Я должен использовать деmonoв в своем коде. Мне нужен демон управления, который постоянно проверяет базу данных для задач и контролирует дочерние демоны. Демон управления должен назначать задачи дочерним демонам, управлять задачами, создавать новые дети, если один из них умирает, и т. Д. Ребенок-демоны проверяют базу данных для задач для них (по PID). Как мне использовать демоны для этой цели?

Daemon – всего лишь кодовое слово для «фонового процесса, который длится долго». Поэтому ответ «это зависит». Perl имеет два основных способа выполнения многопроцессорной обработки:

Многопоточность

Вы запускаете подпрограмму как stream, параллельно с основным программным кодом. (Который может затем просто контролировать состояния streamов).

Накладные расходы на создание streamа выше, но он лучше подходит для многопроцессорности стиля «общей памяти», например, когда вы передаете значительное количество данных взад и вперед. Существует несколько библиотек, которые делают передачу информации между streamами положительно простой. Лично мне очень нравится Thread::Queue , Thread::Semaphore и Storable .

В частности – Storable имеет freeze и thaw что позволяет перемещать сложные структуры данных (например, объекты / hashи) в очереди, что очень полезно.

Пример базовой резьбы:

 #!/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'. Eg #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"; } - #!/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'. Eg #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"; } 

хранимый

Когда дело доходит до Storable, это заслуживает отдельного примера, я думаю, потому что он удобен для перемещения данных.

 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(); 

Поскольку вы передаете объект внутри очереди, вы эффективно клонируете его между streamами. Поэтому имейте в виду, что вам может потребоваться заморозить его и «вернуть» его каким-то образом, как только вы что-то сделали для своего внутреннего состояния. Но это означает, что вы можете сделать это асинхронно, не требуя арбитража блокировки или общей памяти. Вы также можете счесть полезным, чтобы иметь возможность «хранить» и «извлекать» и объекты – это работает так, как вы могли бы ожидать. (Хотя я полагаю, вам может потребоваться быть осторожным в отношении доступности версий модhive и определенных атрибутов, если вы извлекаете сохраненный объект)

Ветвление

Ваш скрипт клонирует себя, оставляя «родителя» и «ребенка» – тогда ребенок обычно расходится и делает что-то другое. Это использует встроенный fork() Unix, который в результате хорошо оптимизирован и, как правило, очень эффективен, но из-за низкого уровня означает, что трудно выполнить большую передачу данных. Вы получите несколько несколько сложных вещей для выполнения Interprocess communication – IPC. (Подробнее см. perlipc ). Это эффективно не в последнюю очередь потому, что большинство реализаций fork() делают ленивую копию данных – пространство памяти для вашего процесса выделяется только по мере необходимости, например, когда оно изменено.

Поэтому очень хорошо, если вы хотите делегировать множество задач, которые не требуют большого контроля со стороны родителя. Например, вы можете fork веб-сервер, потому что ребенок читает файлы и доставляет их конкретному клиенту, а родитель не очень заботится. Или, возможно, вы сделаете это, если хотите потратить много процессорного времени на вычисление результата и только передать этот результат.

Он также не поддерживается в Windows.

Полезные библиотеки include Parallel::ForkManager

Основной пример кода «forking» выглядит примерно так:

 #!/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(); 

Что подходит вам?

Так что трудно сказать, не более подробно о том, что вы пытаетесь выполнить. Вот почему StacKOverflow обычно любит показывать некоторые работы, подходы, которые вы пробовали, и т. Д.

Я бы сказал:

  • если вам нужно передавать данные, используйте streamи. Thread::Queue особенно в сочетании с Storable очень хороша для него.

  • если вы этого не сделаете, forks (в Unix), как правило, быстрее / эффективнее. (Но, как правило, одного достаточно быстро – сначала напишите понятные вещи, и стремитесь к скорости во-вторых. Это не имеет особого значения).

Избегайте, где возможно, нереста слишком много streamов – они довольно интенсивны в памяти и накладные расходы на создание. Вам гораздо лучше использовать фиксированный номер в стиле программирования «рабочий stream», чем повторное создание новых короткоживущих streamов. (С другой стороны, вилки на самом деле очень хороши в этом, потому что они не копируют весь процесс).

Я бы предложил в сценарии, который вы даете – вы смотрите на streamи и очереди. Ваш родительский процесс может отслеживать дочерние streamи через threads -> list() и join или create чтобы сохранить правильный номер. И может передавать данные через центральную очередь в рабочие streamи. Или иметь несколько очередей – по одному на «ребенка» и использовать это как систему назначения задач.

  • Как я могу анализировать даты и преобразовывать часовые пояса в Perl?
  • Какую структуру я должен использовать для написания модhive?
  • Как найти местоположение регулярного выражения в Perl?
  • В чем разница между новыми Some :: Class и Some :: Class-> new () в Perl?
  • Как я должен использовать Perl?
  • Как установить модули CPAN локально без корневого доступа (ошибка DynaLoader.pm line 229)?
  • Как выйти из цикла в Perl?
  • Как я могу вручную интерполировать строковые escape-строки в строке Perl?
  • Как исправить предупреждение о настройке языкового стандарта на Perl?
  • Как я могу поддерживать порядок ключей, которые я добавляю к hashу Perl?
  • Powershell перегружает Perl binmode?
  • Давайте будем гением компьютера.