![]() |
|
more useful options | |
PerlMonks |
Dannyby Danny (Chaplain) |
on Nov 03, 2014 at 19:05 UTC ( [id://1105934]=user: print w/replies, xml ) | Need Help?? |
Below is a method I use to implement a fast web server using Perl with Nginx on a Linux system. I have ran three servers for several years with this and haven't had any issues. I'm posting it here for three reasons: 1) It may be of use to someone. 2) Someone will probably tell me a better way to do it. 3) It will help me remember how to do it if I need to reproduce it. The perl script below is the daemon that runs via systemd. It is started by the systemd service file (/usr/lib/systemd/system/myserver.service) that looks something like:
where /var/www/some/more/paths/Myserver.fcgi is the Perl script below. "myserver" etc should be changed to whatever you want to call things. This service is run automatically by linking via /etc/systemd/system/multi-user.target.wants/. The myserver.socket service file looks something like:
On the Nginx side, the relevant parts of the main server config look like:
where myserver.conf looks like: Perl script, e.g. /var/www/some/more/paths/Myserver.fcgi #!/usr/bin/perl -T # This version uses FCGI and forks. The children use CGI to process stdin. use warnings; use strict; use POSIX ":sys_wait_h"; use FCGI; use CGI (); use DBI; use lib ( $ENV{SOME_PATH} ); use Myserver; delete $ENV{PATH}; my $DEBUG = 0; my %FORKS; my ($SOCKET, $PID_FILE, $debug_fh); if($DEBUG) { $SOCKET = "/tmp/Myserver.debug.socket"; $PID_FILE = "/tmp/Myserver.debug.pid"; my $debug_log = "/tmp/Myserver.debug.log"; open $debug_fh, ">", $debug_log or die "error: can't open file=$debug_log\n"; } else { $SOCKET = "/var/run/www-data/Myserver.fcgi.socket"; $PID_FILE = "/var/run/www-data/Myserver.pid"; } my $LISTEN_QUEUE = 50; $SIG{CHLD} = 'IGNORE'; my %opts = ( note => "just an example" ); my $s = Myserver->new( \%opts ); init_Myserver( $s ); # If needed: set up database connections, constants etc $s->writePidFile( $PID_FILE ); # Optional: If you want to keep a file with the daemon process id. my $socket = FCGI::OpenSocket($SOCKET, $LISTEN_QUEUE); my $req = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket); my $count = 0; while ($req->Accept() >= 0) { $count++; $DEBUG and printf $debug_fh "\n%s FCGI accepted request count=$count ppid=$$\n", scalar localtime; my $pid = fork; if( not defined $pid ) { $s->{CGI} = CGI->new; $s->error("Child failed to fork in Myserver.fcgi. Please email me\@somewhere.com if you get this error.\n"); next; } if($pid) { $req->Detach; # So next Accept doesn't disconnect current request that fork is handling $DEBUG and printf $debug_fh "%s Spawned child cpid=$pid\n", scalar localtime; $DEBUG and printf $debug_fh "%s Detached from request. Waiting for next request.\n", scalar localtime; $DEBUG and $debug_fh->flush; checkForks(); $FORKS{$pid} = time; next; # wait for next request } $s->{CGI} = CGI->new; $s->check_dbh or child_exit($req); $s->{CALL_COUNT} = $count; # Optional: This is for debugging. You can insert an attribute in your HTML to show the call count or do whatever you want with it. $s->init_Myserver_per_request; # Optional: Per request as opposed to the one time init_Myserver( $s ) above. $s->ProcessRequest(); # Do the work on the request. child_exit($req); } sub child_exit { my $req = shift; $req->Flush; $req->Finish; exit; # kill the child } # This is for cleaning up forks that may accumulate because of network issues, # etc. Some debug statements can be added to check if this may be happening. sub checkForks { # Kill forks older than a minute old my $t = time; foreach my $pid ( keys %FORKS ) { if(waitpid($pid, WNOHANG) != 0) { delete $FORKS{$pid}; next; } $t - $FORKS{$pid} > 60 or next; # printf $old_child_fh "%s Child lived more than a minute; might want to investigate.\n", scalar localtime; kill 'KILL', $pid; delete $FORKS{$pid}; } } # This is called when daemon is started, but not when URL requests are being # made, so it's OK to die here if we can't connect to database. sub init_Myserver { # one time init my $s = shift; $s->{VAR} = "some important variable"; $s->{DBH} = $s->DBIconnect(); # If needed # etc } |
|