Using Modcache with MapServer

Suppose you have a setup where you have a dynamic map image rendered by MapServer on a server, and multiple synchronized information displays on which the map should be displayed. Here’s how to use a shared web cache (or web accelerator) in order to reduce load on the MapServer application.

First, some preliminary remarks. If your map is fully or partly composed of static content, you should create a web accessible WMS-C tilecache with TileCache. The TileCache layer can be pre-seeded (pre-populated) through MapServer with tilecache_seed.py. This means that for static layers you don’t even need TileCache or MapServer running on the server at all (which is great, because in my case the server is a resource-constrained embedded platform where python has a footprint that is far too high). OpenLayers supports TileCache layers out of the box via OpenLayers.Layer.TileCache.

For dynamic content, you can use TileCache for caching as well if you are not afraid of running python, but here we are going to use a server-side reverse cache for the map images that are dynamically created by MapServer.

First, it is a good idea to include FastCGI support in MapServer. Head over to www.fastcgi.com and grab their FastCGI library. Building the FastCGI library is trivial:

$ ./configure --prefix=/usr/local
$ make
$ sudo make install

When building MapServer, be sure to enable FastCGI with the --with-fastcgi=/usr/local option.

Verify that your mapserv executable has FastCGI enabled:

$ ./mapserv -v 
MapServer version 6.0.0 OUTPUT=GIF OUTPUT=PNG SUPPORTS=PROJ SUPPORTS=AGG SUPPORTS=FREETYPE SUPPORTS=ICONV SUPPORTS=WMS_SERVER SUPPORTS=FASTCGI INPUT=OGR INPUT=GDAL INPUT=SHAPEFILE

Check if the output of ./mapserv -v contains the item "SUPPORTS=FASTCGI”.

Now you have to tell your webserver how to access MapServer via FastCGI. If you are using Apache, this page should get you started. I’m using Lighttpd instead, so here are the instructions for editing your lighttpd.conf:

1. Append the FastCGI module mod_fastcgi to the server.modules directive:

server.modules += ( "mod_fastcgi" )

2. Add the following lines to your lighttpd.conf

fastcgi.server = ( 
    "/mapserver" =>
    (   "localhost" =>
        (   "socket" => "/tmp/mapserver-fastcgi.socket",
            "bin-path" => “/usr/lib/cgi-bin/mapserv",
            "max-procs" => 1,
            "check-local" => "disable"
        )
    )
)

Using FastCGI, the MapServer process is only created once. Database connections can be cached with the following PROCESSING directive:

PROCESSING “CLOSE_CONNECTION=DEFER”

Now, when sending a request like “http://localhost:81/mapserver?” with your browser, you should see the following message:

No query information to decode. QUERY_STRING is set, but empty.

(That’s OK, MapServer is working but we haven’t supplied it with a meaningful query string)

Now that MapServer is FastCGI-enabled, we can add mod_cache to the picture, assuming you are using lighttpd. If you are using Apache, the squid caching proxy provides similar functionality. mod_cache uses disk caching, but you can use mod_mem_cache for a memory based cache manager if you like.

Unfortunately, mod_cache for lighttpd is not included in the official lighttpd release and is only available as a patch. Download lighttpd with mod_cache support from lighttpd-improved.

Content is stored in the cache using URI based keys. In order to support dynamic generated FastCGI content, cache.dynamic-mode must be enabled. With cache.support-queries enabled as well, modcache will treat “/mapserver?querystr1” and “/mapserver?querystr2” as different resource and save them to different local cache files.

cache.support-queries   = "enable"      # try to cache query with '?'
cache.dynamic-mode      = "enable"      # modcache will treat "/uri?q1" and "/uri?q2" as different resource
cache.max-memory-size   = 2             # number of megabytes for modcache
cache.bases             = ( "/tmp" )    # make sure directory exists and is writeable
cache.debug             = "enable"
cache.refresh-pattern   = (
    "^/mapserver" => "0"      # zero means cache forever
)

Check the lighttpd error log and the mapserver log file in order to verify that your setup works. When sending identical query strings, MapServer should be called only for the first request. All other requests with the same query string should be handled by modcache.

P.S.: The upcoming MapServer 6.2 release includes functionality for tile caching via MapCache. You might want to look into this once it is released.

Leave a Reply