The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition)

19.2. Using FastCGI

The most often criticized shortcoming of CGI is that it requires a new process to be created for every invocation. The effect on performance is significant. The lack of the capability to leave objects in memory between requests can also have a negative impact on design. The combination of these difficulties has led to the creation of FastCGI.

FastCGI is basically nothing more than a protocol definition and software implementing that protocol. Usually implemented as a web server plugin, such as an Apache module, FastCGI allows an in-process helper to intercept HTTP requests and route them via socket to a long running backend process. This has a positive effect on speed compared to the traditional forking approach. It also gives the programmer the freedom to put things in memory and still find them there on the next request.

Conveniently, servers for FastCGI have been implemented in a number of languages, including Ruby. Eli Green created a module (available via the RAA) entirely in Ruby that implements the FastCGI protocol and eases the development of FastCGI programs.

Without going into every detail of how this works, we present a sample application in Listing 19.1. As you can see, this code fragment mirrors the functionality of the earlier example.

Listing 19.1. A FastCGI Example

require "fastcgi" require "cgi" last_time = "" def get_ramblings(instream) # Unbeautifully retrieve the value of the first name/value pair # CGI would have done this for us. data = "" if instream != nil data = instream.split("&")[0].split("=")[1] || "" end return CGI.unescape(data) end def reverse_ramblings(ramblings) if ramblings == nil then return "" end chunks = ramblings.split(/\s+/) chunks.reverse.join(" ") end server = FastCGI::TCP.new('localhost', 9000) begin server.each_request do |request| stuff = request.in.read out = request.out out << "Content-type: text/html\r\n\r\n" out << <<-EOF <html> <head><title>Text Backwardizer</title></head> <h1>sdrawkcaB txeT</h1> <i>You previously said: #{last_time}</i><BR> <b>#{reverse_ramblings(get_ramblings(stuff))}</b> <form method="POST" action="/fast/serv.rb"> <textarea name="ramblings"> </textarea> <input type="submit" name="submit" </form> </body></html> EOF last_time = get_ramblings(stuff) request.finish end ensure server.close end

The first thing that strikes you about this code (if you read the previous section) is a couple of things that you have to do manually in FastCGI that wouldn't have had to do with the CGI library. One is the messy hard-coding of escaped HTML. The other is the get_ramblings method, which manually parses the input and returns only the relevant value. This code, by the way, works only with the HTTP POST methodanother convenience lost when not using the CGI library.

That being said, FastCGI is by no means without its advantages. We didn't run any benchmarks on this example, butit's in the nameFastCGI is faster than normal CGI. The overhead of starting up a new process is avoided in favor of making a local network connection to port 9000 (FastCGI::TCP.new('localhost', 9000)). Also, the last_time variable in this example is used to maintain a piece of state in memory in between requestssomething impossible with traditional CGI.

We'll also point out that it's possible to a limited extent to "mix and match" these libraries. The "helper" functions from cgi.rb can be used on their own (without actually using this library to drive the application). For example, CGI.escapeHTML can be used in isolation from the rest of the library. This would make the previous example a little more readable.

Категории