📜 ⬆️ ⬇️

Chat with fastcgi manager

Description


A small extension for FCGI :: ProcManager , which allows you to access the manager of fcgi processes. For communication of the third-party program with the manager a socket is used.

Underwater rocks


The FCGI :: ProcManager module is used to spawn inbound request handlers. The current process is a manager. From the start, it spawns handlers (n_processes pieces), then it maintains their number, following the dead in battle. For these purposes, he uses the wait . Here lies the problem. After threads are started, the manager, causing wait, is blocked. You can reach him only through signals . It is necessary to execute the code wisely and carefully in the signal handler, it is not good to drive a shit code there. So it is necessary to establish another communication channel.


Implementation


For example, let's try to make the manager listen to the socket. For this we need to force the manager not to block. The function pm_wait is responsible for waiting in FCGI :: ProcManager:
sub pm_wait { my ($this) = self_or_default(@_); # wait for the next server to die. next if (my $pid = wait()) < 0; # notify when one of our servers have died. delete $this->{PIDS}->{$pid} and $this->pm_notify("server (pid $pid) exited with status $?"); return $pid; } 

')
It is necessary to replace the wait call with waitpid and insert our code there. Our manager should open and listen to another port, and in the standby mode, periodically look for descendants and listen to the port. Let's write our own version of the pm_wait function and substitute the function of the same name in the FCGI :: ProcManager module using the monkey patch . In response to the messages, we will overload all descendants, having previously updated the project libraries.
 use FCGI; use FCGI::ProcManager; use IO::Select; use IO::Socket; use POSIX ":sys_wait_h"; #use Module::Refresh; use feature ':5.14'; use strict; # For fastcgi app my $socket = FCGI::OpenSocket( ":9019", 5 ); my $request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket, FCGI::FAIL_ACCEPT_ON_INTR ); # To communicate with the manager my $server = IO::Socket::INET->new(LocalPort => '9034', Type => SOCK_STREAM, Reuse => 1, Listen => 1) or die "Couldn't start messange server $@\n"; my $select = IO::Select->new($server); # Patch my $pm_wait = sub { my ($pm) = @_; my $pid; while (1) { last if ($pid = waitpid(-1, WNOHANG)); foreach my $client ($select->can_read(1)) { if ($client == $server) { $client = $server->accept(); $select->add($client); } else { my $msg = <$client>; chomp $msg; if ($msg eq 'reload') { #my $refresher = Module::Refresh->new(); #my @module_list = qw( MyLib1.pm My/Lib2.pm ); #foreach my $module_name (@module_list){ # $refresher->refresh_module($module_name); #} $pm->sig_manager('HUP'); } print $client "done!\n"; $select->remove($client); close $client; } } } # notify when one of our servers have died. delete $pm->{PIDS}->{$pid} and $pm->pm_notify("server (pid $pid) exited with status $?"); return $pid; }; no strict 'refs'; *{'FCGI::ProcManager::pm_wait'} = $pm_wait; use strict 'refs'; my $pm = FCGI::ProcManager->new( ); $pm->pm_manage( n_processes => 3, pm_title => 'perl-fcgi-pm', die_timeout => 10 ); while ( $request->Accept() >= 0 ) { $pm->pm_pre_dispatch(); print "Content-Type: text/plain\n\n"; print "server works"; $pm->pm_post_dispatch(); } FCGI::CloseSocket($socket); close $server; 


Following is the code for the test:
 use IO::Socket; use feature ':5.14'; use strict; my $socket = new IO::Socket::INET(Proto => "tcp", Type => SOCK_STREAM, PeerPort => 9034, PeerAddr => "127.0.0.1" ) or die "Can't connect: $!"; #my $msg = $ARGV[0]; my $msg = 'reload'; if ($msg) { print $socket "$msg\n"; my $answer = <$socket>; say "Answer: ".$answer; } 


Total


Thus, we have the opportunity to send messages (you can think up the protocol yourself) to the manager of fastcgi processes. This can be used to poll the current configuration of the application. Reload project libraries (commented out in the example). You can increase / decrease the number of handlers
 $pm->{n_processes} = 10; $pm->sig_manager('HUP'); 

Well, little things like profiling on / off. Of course, these tasks can be solved in other ways.

There's more than one way to do it.

Source: https://habr.com/ru/post/124691/


All Articles