FastCGI

From DreamHost
(Redirected from Fastcgi)
Jump to: navigation, search

Overview

FastCGI is a way to have CGI scripts execute time-consuming code (like opening a database) only once, rather than every time the script is loaded. In technical terms, FastCGI is a language independent, scalable, open extension to CGI that provides high performance without the limitations of server specific APIs.

Language-specific code guides

If you wish to get started quickly, these are the best places to start:

Configuration details

This is the current FastCGI (mod_fastcgi) configuration at Dreamhost:

FastCgiConfig -autoUpdate -initial-env RAILS_ENV=production -idle-timeout 60 -startDelay 10 -maxClassProcesses 2 -killInterval 300

These settings are almost exactly the default configuration for FastCGI. Details about this configuration line can be found in the mod_fastcgi documentation.

On DreamHost PS, FastCGI is handled by mod_fcgid rather than mod_fastcgi. DreamHost has also began the process of migrating the shared hosting servers.

mod_fcgid is an Apache module providing a FastCGI interface. It's an alternative to mod_fastcgi that is specifically tuned for the dynamic FastCGI configuration used on DreamHost servers.

Making changes to your scripts

Structure of a FastCGI script

FastCGI scripts have two sections:

  1. Code that runs just once (like opening a database).
  2. Code that runs every time.

Here's an example with Perl:

#!/usr/bin/perl

#-----------------------
#  CODE THAT RUNS ONCE
#-----------------------
    $counter=0;

#---------------------------------
#  CODE THAT RUNS EVERY TIME
#---------------------------------

use FCGI;
while ( FCGI::accept() >= 0 ) {
     $counter++;
     print "Content-type:text/html\n\n";
     print "I have run $counter times.";
}

While you might think that you need to print the header only once, in fact the header must be included in the "run every time" section.

No exits

If your script terminates by some command (such as exit; or last; in Perl), the script starts over from scratch the next time it's run, losing the benefits of running certain code only once. Additionally, if a script is terminated this way it may not run again for several minutes.

The symptom is that the browser tries to load the page for a couple of minutes, finally giving up with "an error occurred while processing this directive". The error is even more frustrating because you (or your users) have to wait multiple minutes to get it.

The solution is to use whatever flow control is available in your programming language to have the script finish by itself. Here's a before and after example in Perl with some pseudocode:

Wrong way
#!/usr/bin/perl

#-----------------------
#  CODE THAT RUNS ONCE
#-----------------------
     (open a database)

#---------------------------------
#  CODE THAT RUNS EVERY TIME
#---------------------------------

use FCGI;
while ( FCGI::accept() >= 0 ) {
      print "Content-type:text/html\n\n";

      if (some test) { exit;}

      more code;
}
Right way
#!/usr/bin/perl

#-----------------------
#  CODE THAT RUNS ONCE
#-----------------------
     (open a database)

#---------------------------------
#  CODE THAT RUNS EVERY TIME
#---------------------------------

use FCGI;

while ( FCGI::accept() >= 0 ) {
      print "Content-type:text/html\n\n";

      if (some test) { goto(EXITLABEL) }

      more code;

      EXITLABEL: {}
}

Using $ENV{'QUERY_STRING'} instead of @ARGV

You cannot get the query string in FastCGI by using the @ARGV array. You must use $ENV{'QUERY_STRING'} instead.

Errors and troubleshooting

Bad scripts won't return an error immediately; you must wait a couple of minutes for the script to time out. If the script is on a web page, the page appears to hang – and even when the script times out, the actual error message might not be logged and you may only see a 500 error. This means that testing under FastCGI is rather impractical. Development should be done with standard CGI, or on your local computer. FastCGI should be added after the script has already been debugged.

Also, while it's tempting to develop on a live server, it's dangerous and makes you a bad neighbor for shared hosting.

To see immediately if a script will fail (won't check all cases because %ENV is different), test it from the command line:

perl ~/example.com/myscript.fcgi

or, for deeper checking if you can use the debugger:

perl -d ~/example.com/myscript.fcgi

If you've introduced an error into a script, fixing the error might not fix it immediately, because sometimes the server is still running an old version of the script from memory for various reasons. (The server is supposed to look at the modification date of the file so that it's always running the most recent version, but this is often not the case. When the server fails to do this, "touch"ing the file doesn't help, either.)

To make the server run the current version of the script, find the old process and kill it:

killall -USR1 scriptname.fcgi     (replace scriptname.fcgi with the name of the process)

The above command may not always convince all the processes to stop. To force them to stop use:

killall -9 scriptname.fcgi   (replace scriptname.fcgi with the name of the process)

Occasionally, you may need to use ruby1.8 as a task ID:

killall -9 ruby1.8

My FastCGI Isn't working

Make sure that the script you are trying to execute is set as executable:

chmod +x myscript.fcgi

Check the error log if you are getting the error:

[Fri Sep 05 11:55:03 2008] [error] [client 123.45.67.89] FastCGI: comm with (dynamic) server "/home/myaccount/example.com/myscript.fcgi" aborted: (first read) idle timeout (60 sec)

Make sure you check your file and directory permissions. The directory containing the fcgi, and the fcgi itself, must be owned by your default unix group and be group-writable:

chgrp -R `groups | awk '{print $1}'` ~/example.com/ 
chmod -R g+w ~/example.com/

Also, check to see if you have a similar directive:

RewriteRule ^(.*)$ scriptname.cgi [QSA,L]

That needs to be changed to:

RewriteRule ^(.*)$ scriptname.fcgi [QSA,L]

Finally, try deleting all sessions in your /tmp folder (if you’re on a VPS or Dedicated server).

See also