Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Pre-Forking Daemon with Parallel::ForkManager

by enemyofthestate (Scribe)
on Oct 17, 2019 at 20:40 UTC ( #11107627=perlquestion: print w/replies, xml ) Need Help??

enemyofthestate has asked for the wisdom of the Perl Monks concerning the following question:

Has anyone used, or have an idea how to use, Parallel::ForkManager in a pre-forking daemon? I know how to do it using an adapted formula 17.12 from Perl Cookbook but P::FM is a pretty slick way to handle the forks. I have only used it in client programs where it works very well.

  • Comment on Pre-Forking Daemon with Parallel::ForkManager

Replies are listed 'Best First'.
Re: Pre-Forking Daemon with Parallel::ForkManager
by stevieb (Canon) on Oct 17, 2019 at 23:35 UTC

    What do you mean by "pre-forking daemon"?

    I've used Parallel::ForkManager in a client script that creates a set, configurable number of forks, where each fork is responsible for dispatching a set of commands to back-end daemons running on several remote listening servers and then aggregating the results, but I'm unsure of what you're wanting here.

    Could you describe your situation/scenario a little bit as to what you're wanting to achieve? Or is this just something that you found interest in and desire to create a problem that you want to use P::FM to solve?

      What I understand by "pre-forking daemon" (or more usually, "pre-forking server") is a process with a dynamic number of forked children which manages the child pool so that there are always some idle children waiting for client requests. This is done as a compromise to improve latency without committing vast amounts of memory. If there were a set number of children (say 64) then you might have a situation where all 64 of them are sitting around doing nothing just waiting for a request and hogging memory while it happens. OTOH, if you only fork a child when a request comes in the heavy act of forking introduces latency in the response. By having a stipulated, configurable range of "spare" children you get the best of both worlds. That's the theory, anyway.

      Apache 1.x used this method and you can still run Apache 2.x this way if you choose (but most use a threaded MPM instead). PHP-FPM seems to use a similar mechanism as do some nameservers, etc.

      Sure.

      I am creating a server application that receives a street address over a socket, normalizes the address, gets the co-ordinates of the property, and grabs spatial and parcel data from an Oracle database. It then returns the information in one of several formats requested by the caller. It has to handle mutiple simultaneous requests. For technical reasons related to the way the queries have to be made I prefer it to be a forking server instead of multi-threaded.

      In the past I've used a modified version of the formula in the Perl Cookbook but P::FM seems like a more elegant solution and I have used it sucessfully in several client programs. It really simplifies the fork management and that may make it easier for some future maintainer to support. The actual logic for processing the information is in its own subroutine so the desire is also one of making the code "prettier".

      Once upon a time I wrote similar programs in C so I am not completely unfamiliar with the book-keeping necessary to make this work.

        Your application sounds like a perfect use case for an application speaking REST (or not) served by one of the modern web application frameworks, e.g. Dancer2 or Mojolicious, which come with preforking servers built in. It's not necessary to build your own frameowrk for that part of your application.

        Hope this helps!


        The way forward always starts with a minimal test.

        Maybe Minion might be of use? (disclaimer: never personally used it, just seen it mentioned browsing the Mojo docs)

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

Re: Pre-Forking Daemon with Parallel::ForkManager
by davido (Cardinal) on Oct 18, 2019 at 18:41 UTC
Re: Pre-Forking Daemon with Parallel::ForkManager
by ikegami (Pope) on Oct 19, 2019 at 13:37 UTC
    my $pm = Parallel::ForkManager->new(NUM_CHILDREN); for (1..NUM_CHILDREN) { $pm->start and next; my $client_sock = $server_sock->accept or die $!; ... $pm->finish; }

    Note: It'll use a new process for each request, but the process will be created as soon as the previous request is complete (even if no requests are pending).

Re: Pre-Forking Daemon with Parallel::ForkManager
by trippledubs (Deacon) on Oct 19, 2019 at 16:45 UTC

    If P::FM doesn't work out, Thread::Queue or IPC::Msg having multiple workers waiting to service a queue might also present an elegant solution, though probably harder to implement correctly.

Re: Pre-Forking Daemon with Parallel::ForkManager
by tybalt89 (Parson) on Oct 19, 2019 at 15:16 UTC

    Do you need/want it to dynamically adjust the number of children depending on how "busy" it is, or is it enough to have a fixed number of children?

      For this application a fixed pool will work well enough.

        Ok, that's the simpler version. I don't know if it can be done with Parallel::ForkManager ( and don't really care ), so here's a version using my own Forking::Amazing. There are some tune-able parameters that can be adjusted for best performance in your situation. It is pretty simple with a good forking package :)

        #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11107627 use warnings; use IO::Socket; use Forking::Amazing; my $maxforks = 20; # tune these ... my $clientsperchild = 300; my $port = 6666; my $listen = IO::Socket::INET->new(LocalPort => $port, Listen => 20, Reuse => 1) or die "$@ on listen create"; Forking::Amazing::run $maxforks, sub { print "child $$\n"; process( scalar $listen->accept or return [] ) for 1 .. $clientspe +rchild; return []; }, sub { push @Forking::Amazing::ids, 1 }, (1) x $maxforks; sub process # custom code goe +s in here { my $socket = shift; my $query = <$socket> // return []; # read query print $socket "prefork $$ is answering: $query"; # reply }

        And here's the program I used to stress test it. I've been running it with 100 forks and 100 transactions per fork for a total of 10,000 transactions.

        #!/usr/bin/perl use strict; use warnings; use IO::Socket; use Forking::Amazing; my $forks = shift // 1; my $sends = shift // 1; my $totalerrors = 0; my $transactions = 0; Forking::Amazing::run $forks, sub { my $errors = 0; my $trans = 0; for ( 1 .. $sends ) { my $s = IO::Socket::INET->new('localhost:6666') or die; my $key = join '', map +('a'..'z')[rand 26], 1 .. 10; print $s "foo|$key\n"; my $answer = join '', <$s>; $answer =~ /$key/ or $errors++; print $answer; close $s; $trans++; } return [ $errors, $trans ]; }, sub { $totalerrors += $_[1][0] // 0; $transactions += $_[1][1] // 0; }, 1 .. $forks; print "total errors: $totalerrors transactions: $transactions\n";

        And here's the Forking::Amazing module

        And if you've read this far, here's a version that does variable numbers of children depending on how busy it gets. This version tests OK, but there my be some corner cases I haven't discovered yet. It also has parameters that will probably need tweaking.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11107627]
Approved by johngg
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (8)
As of 2019-12-11 17:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?