FastCGI

From DreamHost

Jump to: navigation, search

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. To use FastCGI you will need to:

  1. Enable it in the control panel under Domains -> Manage Domains -> Web Hosting -> Edit.
  2. Make some minor changes to your scripts (detailed below).
  3. Name your script with an .fcgi extension, or add the line "SetHandler fastcgi-script" to your .htaccess file. (One user says editing .htaccess this way might cause problems depending upon where you put your files.)

In technical terms, FastCGI is a language independent, scalable, open extension to CGI that provides high performance without the limitations of server specific APIs. fastcgi.com, fastcgi.com FAQ.

Contents

Language-specific code guides

If you want to get started quickly, these are the best place 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

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

Note, the settings may change at any time without notice though Dreamhost says they have no plans to change it.


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

Changes to make 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 have 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 (like exit; or last; in Perl), the script will start over from scratch the next time it's run, losing the benefits of running certain code only once. What's more, in my experience 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 doubly 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;

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

      if (some test) { next REQUEST }

      more code;

}

Use $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.


Error Reporting

One annoying thing is that errors, even compile time ones, aren't reported until the script has had a chance to time out - 120 seconds, no less. Even then, the actual error message might not be logged, you might get only the 500 error. This means that testing under FastCGI is rather impractical. Development should be done with standard CGI, or with FastCGI disabled, 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 will be different), test it from the command line.

 perl ~/mydomain.com/myscript.fcgi

or for deeper checking if you can use the debugger--

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


Restarting FastCGI processes

If you edit a script then the server will restart it from scratch the next time it's called, running the "run once" code again from the top. The server knows to do this by looking at the modification date of the script. If the process doesn't restart for some reason then you can try editing the file again so that it has a different modification date. A geeky way to accomplish the same thing is to use the Unix touch command on the file. For example:

touch ~/domain/path/index.fcgi


Stopping FastCGI processes

The following command will tell the FastCGI process to end after it has finished any requests it is handling.

killall -USR1 dispatch.fcgi

(replace dispatch.fcgi with the name of the process)

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

killall -9 dispatch.fcgi

(replace dispatch.fcgi with the name of the process)

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

killall -9 ruby1.8

My FastCGI Isn't working

Check your .htaccess file for

AddHandler fastcgi-script .fcgi
Options +FollowSymLinks +ExecCGI

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/mysite.com/myscript.fcgi" aborted: (first read) idle timeout (60 sec)

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

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

If you have edited your PHP.INI file, then you need to force a recache... load your terminal and type:

touch ~/php5/php5-wrapper.fcgi

Also, I found that I had the following directive:

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

That needed to be changed to:

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


Delete the sessions in your /tmp folder

Possible future alternatives to using Fast CGI

http://www.vmunix.com/mark/blog/archives/2006/01/02/fastcgi-scgi-and-apache-background-and-future

Improving FastCGI on Dreamhost

There are now three suggestions up on the Suggestions sections of the panel:

These three would benefit deployment and use of certain application servers -- there's a problem w/ restarting processes correctly when deploying e.g. TurboGears using FastCGI, plus the memory consumption is 3x as large (with three forked processes as the current configuration on server glass).

Killed incorrectly, you'll have to wait for a couple of minutes (I *think* it's the Apache FastCGI timeout) before the application will start again. Also, the fact that you run N forked servers will force you to store all data in a database instead of keeping the state in the server, which is one of the main points of having an application server -- you have *state*.

Some other web hosts (linked from the TurboGears official site) assign port numbers to apps/websites to which the app is connected using an (optional) autostart CGI, bootstrapping the app server, through a reverse proxy using Apache/mod_proxy. Very easy and very clever. Would be nice to see something similar at DreamHost.


External Links



Personal tools