Digital Solutions & Online Creative 

Alex runs a small digital creative business from an office in London. It's called Outside In Media.

About

SOYREX is a web development and design resource intended as a place for me to share tips and tricks relating to html, css, web design, web development and other internet and web topics. If you like what you read, leave a comment, or send an email. Also, check out my company web site.

This form does not yet contain any fields.
    Search the Archives

    Online Schools

    Currently Reading..
    • Blink: The Power of Thinking Without Thinking
      Blink: The Power of Thinking Without Thinking
      by Malcolm Gladwell

      Blink talks about flash cognition and sub-conscious cognitive activity.. awesome read!

    • Confessions of an Economic Hit Man: The Shocking Story of How America Really Took Over the World
      Confessions of an Economic Hit Man: The Shocking Story of How America Really Took Over the World
      by John Perkins

      Confessions of an Economic Hit Man - i knew the world was a big conspiracy.. but this is a gripping insight into how the world really works.

    Recommended Reading
    • Designing with Web Standards (Voices That Matter)
      Designing with Web Standards (Voices That Matter)
      by Jeffrey Zeldman, Ethan Marcotte
    • Web Standards Creativity: Innovations in Web Design with XHTML, CSS, & DOM Scripting: Innovations in Web Design with XHTML, CSS, and DOM Scripting
      Web Standards Creativity: Innovations in Web Design with XHTML, CSS, & DOM Scripting: Innovations in Web Design with XHTML, CSS, and DOM Scripting
      by C et al Adams
    Thursday
    Aug282008

    Django, Nginx + Memcached 

    Ok, so Django has a pretty good caching engine build right into the framework, right? So why would i be trying to implement a caching solution for my Django project? I'm a masochist. that's why.

    My sites are hosted on a VPS (with very sparse memory allocation), so i'm looking to minimise memory usage wherever possible. I also had the joy of watching my Django FCGIs for http://the-hive-mind.com get completely annihilated by metafilter.org a few weeks ago - i think it stood up to the first 200-300 requests.. then completely died.

    Anyway, I have just launched a website for a friend in Nepal, who runs a trekking business (he comes highly recommended if you ever want to go hiking in that region). Basically, I decided to try and load test the Django instances on the site, to see how much traffic it could handle... i downloaded and installed siege.. which (as the name suggests) lays siege to your web server.

    My out of the box Django FCGI being called by a Nginx instance was only capable of reliably handling up to about 10 concurrent connections before it started dropping requests.. not really acceptable (caching, at this point.. was completely turned off).

    So I set about sorting out caching. I read the Django documentation.. and quickly decided that the built in caching wasn't quite to my liking. I already knew a little about memcached, and wanted to use it to cache my generated responses, so the fact that Django supported it was nice. However the idea of using a the Django cache middleware doesn't really cut it. Nginx supports memcached.. so why would i want to fire the request off to my (inherently bulky and inneficient) python FCGI instance? just to use python's undoubtedly slower memcached library to return the cached content? I wouldn't.

    The solution I've come up with is somewhat simplistic, however it DOES solve my immediate problem... and it's done wonders for the server's load capacity.

    The solution : Django creates the cache object, but Nginx retrieves it.

    The Django caching middleware is halfway to the perfect model, it correctly creates cached objects, however it uses a strance combination of . separated python words, the URI and a md5 hexdigest. That all seems like a little much to expect Nginx to replicate (remembering that my goal here is to avoid hitting the FCGI at all for cached content).

    So after some digging and background, I decided it would be fun to write a simple MiddleWare for Django that would allow me to cache out my content's responses with nice, Nginx friendly keys, so I could then implement my cache override directly in my Nginx config.

    Step 1: Creating the Cache Objects: MiddleWare

    This is really very simple. I'd never written Django Middleware before, however it was surprisingly simple. A MiddleWare is just a Python object that implements any of a series of methods. My NginxMiddleWare looks like this:

    from django.core.cache import cache
    import re
    import settings
    
    class NginxMemCacheMiddleWare:
        def process_response(self, request, response):
            cacheIt = True
            theUrl = request.get_full_path()
    
            # if it's a GET then store it in the cache:
            if request.method != 'GET':
                cacheIt = False
    
            # loop on our CACHE_INGORE_REGEXPS and ignore
            # certain urls.
            for exp in settings.CACHE_IGNORE_REGEXPS:
                if re.match(exp,theUrl):
                    cacheIt = False
    
            if cacheIt:
                key = '%s-%s' % (settings.CACHE_KEY_PREFIX,theUrl)
                cache.set(key,response.content)     
    
    
            return response
    

    We also need to install our new MiddleWare into the site. I saved the above class definition into a file called NginxMiddleWare.py and installed it into my site-packages.. i intend to implement this caching scheme on my other Django sites (including this blog). So in my settings.py I add 'NginxMiddleWare.NginxMemCacheMiddleWare' to the MIDDLEWARE_CLASSES.

    Also in the Django projects settings file we add the following:

    CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
    CACHE_KEY_PREFIX = '/your-site-name'
    CACHE_IGNORE_REGEXPS = (
        r'/admin.*',
    )
    

    Firstly we are telling Django to use memcached (I'm assumign you already have memcached set up - go here if you don't).

    I have defined two new settings variables, CACHEKEYPREFIX and CACHEIGNOREREGEXPS. These allow me to control the caching. CACHEKEYPREFIX allows me to store multiple sites in the same memcached.. but creating a unique string key. And CACHEIGNOREREGEXPS allows me to define a set of regular expression URLS that I DO NOT want to cache - like the admin site.

    Step 2: Nginx Configuration

    The configuration of Nginx was a bit fiddly. I really wanted my Django project to continue to run in exactly the same way as it was previously - ie no silly fake URL prefixes or other cruft to confuse my urls.py..

    So I needed to get Nginx to firstly serve my Django pages off a fake, internal server, like so:

    server {
            listen 9004;
            location / {
    
                    # host and port to fastcgi server
                    # @fastcgi_pass unix:/var/www/trekkingnepaltours.com/django.sock;
                    fastcgi_pass 127.0.0.1:8004;
                    fastcgi_param PATH_INFO $fastcgi_script_name;
                    fastcgi_param REQUEST_METHOD $request_method;
                    fastcgi_param QUERY_STRING $query_string;
                    fastcgi_param CONTENT_TYPE $content_type;
                    fastcgi_param CONTENT_LENGTH $content_length;
                    fastcgi_pass_header Authorization;
                    fastcgi_intercept_errors off;
                       include /etc/nginx/fastcgi_params;
                    }
    
    }
    

    Pick a high port, then configure your FCGI however you like it to be configured, above is what I use.. but I'm assuming if you've read this far that you know enough to configure your FCGI under Nginx.

    Basically this creates us another server that we can talk to, allowing us to use Nginx like a proxy to server our Django pages. The logic is something like:

    check if url is cached
    if url IS cached then
        return the cached response
    else
        proxy the connection to our django server
    

    So the guts of the logic for hte Nginx config are as follows:

       location / {
                if ($request_method = POST) {
                        proxy_pass http://localhost:9004;
                        break;
                }
                default_type  "text/html; charset=utf-8";
                set $memcached_key "/your-site-name-$uri";
                memcached_pass localhost:11211;
                error_page 404 502 = /django;
        }
    
        location = /django  {
                proxy_pass http://localhost:9004;
                break;
        }
    

    Before this definition, i have a whole bunch of locations set to handle my static content, so it never reaches this stage. The / location first checks if it's a POST request, if so it will proxy the request off to django directly, we never cache POSTs. Then if we get past that point, we set the default type of our response to html and utf8. Then we set $memcachedkey to the string that we used in our Django settings.py plus a dash plus the $uri from nginx. Next we pass off to memcached locally to check for the cached object, if it exists memcached will return it, otherwise we get an error. Errors are handled by our errorpage directive, which farms them off to a virtual location called /django, which again sends the request to the internal Django instance.

    So, if there is no cached object, then Nginx will get Django to render the page, and the MiddleWare we defined above will save off our cached objects with the correct keys.

    But Wait - what if I change data in the DB?

    Of course, Django is a Content Management Framework and i make extensive use of the provided admin system.. so when I add a new trek to http://trekkingnepaltours.com i want the site to update itself. To achieve this we override the save methods on the relevant DB models and get them to clear the cache.

    I assume here that you use the standard Django convention of defining a getabsoluteurl method on your models, so we just override the save function and call our django cache delete function on the correct cache key, to remove if from memcached. Below is the save method off my Photo model:

    def save(self):
        theUrl = self.trek.get_absolute_url()
        key = '%s-%s' % (settings.CACHE_KEY_PREFIX,theUrl)
        cache.delete(key)
    
        key = '%s-/' % (settings.CACHE_KEY_PREFIX)
        cache.delete(key)
    
        super(Photo, self).save()
    

    As you can see, I'm removing the cache for the Trek model that this photo is associated to (since that's where the actual page is) and also the cache for the home page, the photos often get rendered there too - obviously we can be as granular as we like in this save method, removing whatever we need to in order to update the site. We could probably even write some code to wipe the entire cache..

    The end result

    Siege now shows the site successfully handling 2000 odd concurrent connections on a constant load for 1 minute... while hardly registering any memory usage at all on my VPS - problem solved.

    Disclaimer..

    I know this is probably not the most elegant solution, however it has solved my problem for me. Having said that, if you have ANY comments about how this could be made better, please leave a comment... also if Django can already do this out of the box... someone smarter than me needs to tell me how ;)

    PrintView Printer Friendly Version

    EmailEmail Article to Friend

    Reader Comments (21)

    Hi,
    Thanks for the nice Idea.
    I have a travel company / adventure travel in the Himalaya and it is usually to encounter different kinds of problems with the website and the net.
    Thanks

    Prem

    November 12, 2009 | Unregistered CommenterPrem

    Very nice solution, just found this on Google. I was thinking of doing something similar--I saw someone do something like this with RoR--but I'm glad I found this first, it's a good guide to doing it right.

    March 9, 2010 | Unregistered CommenterZack

    Hey Zack,

    Well.. i don't know if i'm "doing it right", but the setup in this post is still running and my sites are still up.. so i guess it does work ;)

    March 11, 2010 | Registered Commenterrex

    excellent solution....

    Thanks for sharing.

    March 22, 2010 | Unregistered CommenterContus

    Thanks for the useful code and will try this code on my site, make sure it will help to running up for my site.

    your crazy! thank you though for explaining how to do it!!

    April 8, 2010 | Unregistered Commenterweb design los angeles

    Good site you have here :)

    Looking for proxy to unblock sites?

    Try this one http://.4ever3.info

    Hope it helps :)
    cheers!

    June 7, 2010 | Unregistered CommenterFrancisssso

    In order to meet the needs of most women, Christian Louboutin hot sale now! All for wholesale can help most women to achieve their dreams. Weoffers Christian Louboutin Pumps high heels, boots, sandals, pumps, wedges, platform shoes.35%discount. Christian Louboutin Boots Shoes with cheap price online. In the Christian Louboutin Sandals buy, Every female can buy what they want and Cheap Louboutin Shoes.

    Fro Coach handbags straw tote to the Coach Bonnie black patent leather straw tote, and the Coach Wristlet , Coach has an array of ways to provide straw a striking look. It has a fabulous look that you are going to be mesmerized to get it at any cost when you see it. Happily, the product has been reasonably rated. So, when you fall madly in love with these Coach Ergo , you will not mind spending a little adds up to purchase them.

    ED clothing is the very respectable clothing line that never out of fashion. Prices are rather steep but the clothing is durable and stylish. Unfortunately, finding cheap ED Hardy Shoes is difficult. There are many hardy shirt out there so beware on your search unless you're buying directly from the website. If you are buying from another site, use these tips to decipher the ED Hardy Shoes clothing from the fakes.

    July 12, 2010 | Unregistered Commenterbreitling watchs

    Discount Louis louis vuitton handbags Vuitton Handbags,Wallets & Purse louis vuitton Online Store. In

    louis vuitton replica handbags the my lv replica handbags store buy designer handbags cheap Louis louis vuitton handbags Vuitton Handbags, You replica handbags best choice.louis vuitton,louis vuitton handbags,louis vuitton handbag,louis louis

    vuitton handbags vuitton bags,louis vuitton bag,louis vuitton wallets,louis vuitton wallet,gucci,gucci wholesale louis vuitton

    handbags handbags,gucci handbag,gucci bags,gucci bag,gucci wallets,gucci wallet,prada,prada handbags,prada handbag,prada bags,prada wholesale replica handbags bag,prada wallets,prada wallet,buy prada handbag.
    815GSW0715

    July 15, 2010 | Unregistered CommenterReplica Handbags

    Nice tips.
    seo agency

    July 20, 2010 | Unregistered CommenterKate

    we like coach handbags

    July 23, 2010 | Unregistered Commentercoach handbags

    Welcome to <A href="http://www.b2bsharing.com/christian-louboutin-shoes-c-4463.html">Christian Louboutin Shoes</A> on-line store! Now an extensive selection of super <A href="http://www.b2bsharing.com/christian-louboutin-shoes-c-4463.html">Fashion Christian Louboutin Shoes</A> with different colors and styles are provided at the most competitive prices. You can buy <A href="http://www.b2bsharing.com/christian-louboutin-shoes-christian-louboutin-pumps-c-4463_4466.html">Christian Louboutin Pumps</A>,<A href="http://www.b2bsharing.com/christian-louboutin-shoes-christian-louboutin-boots-c-4463_4465.html">Christian Louboutin Boots</A>,<A href="http://www.b2bsharing.com/christian-louboutin-shoes-christian-louboutin-sandals-c-4463_4467.html">Christian Louboutin Sandals</A> at our online store. Our goal is to make all of you enjoy the top fashion and become the most charming women with our beautiful shoes.The world's best <A href="http://www.b2bsharing.com/christian-louboutin-shoes-c-4463.html">wholesale Christian Louboutin Shoes</A> website.Buy now!

    July 24, 2010 | Unregistered Commenterhei

    we offer cheap and high quality workout-px90 dvd
    insaity workoutLet you have vigor
    cheap insaity workout Let you be stronger
    workout-px90 Lets you easily fitness
    px90-schedule keep you motivated
    px90-insanity make you healthier
    vibram fingersdifferent designs
    five fingers runingPersonality
    barefoot runninghigh quality
    Mbt shoesspecial style
    Mbt safiridifferent designs
    MBT ChapaPersonality
    welcome to buy!thank you!

    July 27, 2010 | Unregistered Commenterinsanity workout

    Suppose we find a Porsche automobile parking in the roadside, we generally consider the exact proprietor should be a millionaire. That's correct mainly because any type of Porsche car is close to million RMB. Today, the exact branded cars and replica wyler watches have become the exact symbolic representation of social status and personal identification. Either is the exact need to for the exact rich people replica versace watches. Suppose a famed luxury view combines with the exact luxury car, what effect will cause? Needless to say, it should be fantastic.

    July 27, 2010 | Unregistered Commenterivan.watcheslux

    In our BagOnHand which also is fantastic Louis Vuitton online store. you will find discount LV wallets,handbags, bags ,luggage for best deals.It is real eye-opener for newest design to U and latest releases.We offer wholesale cheap price with first grade material.Wonderful combination of letter symbols "Louis Vuitton"(LV) with classical Louis Vuitton monogram pattern and interesting cartoons make LV purses give off new luster and brilliance.The Louis Vuitton purse and handbag, the appearance is very attaticve and elegant .discount handbags It is for easy, Women's Handbags city chic fee,Designer Handbags the "Louis Vuitton"(LV) combines sophisticated comfort and detailing of the handbag and make the handbag perfect for everyday useIt include :Gift Handbag,LV Tag,Care Booklet,women's wallets Dust Bag,Identity Card in Envelope with handbag.

    July 27, 2010 | Unregistered Commenterbagonhand

    I'm really impressed that there's so much about this subject that's been uncovered and you did it so well, with so much class.
    SEO Services India

    July 27, 2010 | Unregistered Commentercadee

    mmovv.com provides massive virtual currency for MMORPG (massive multi-player online role playing game). Such as
    knight noah
    dragonica gold
    lotro gold
    2Moons dil
    maplestory mesos
    dfo gold
    atlantica gold
    2moons dil
    maplestory mesos
    metin2 yang
    mabinogi gold
    tera gold
    shaiya gold
    Metin2 Yang
    aika gold
    aion kina
    lineage 2 adena
    mabinogi gold
    dofus kamas
    blade soul gold
    c9 gold
    dragon nest gold
    last chaos gold
    hero 108 gold
    maplestory mesos
    rom gold
    ffxiv gil, there are 84-game list till now. You can nearly find all the games here. All of them are fantastic, and when you have more currency in game that we deliver to you, you can enjoy the game much more.

    mmovv.com provides not only virtual items and currency service for multi-players on the internet, but also a massive trading platform where you can buy the items and currency from us, also can sell them to us.

    At the same time, mmovv.com provides positive gaming service for you, creating a healthy, casual, and harmonious game environment where you will have more fun. So consumption on our website is an enjoyable thing.

    Many interesting games here and I can't introduce all of them to you in the little space. So just come to our support live chat if you have any problems or contact us by the other way. We promise we will give you best satisfied service. And we believe you are our valued customer in the future and we will have lasting long time business and good friendship.

    July 28, 2010 | Unregistered Commenterasd

    edhardy provides you the best and cheapest edhardy jeans. All the

    products at http://www.jeans-edhardy.com are 100% high quality, competitive price, free shipping with best and timely

    service, 100% satisfaction. Big discount for big order. Welcome to make your choose.

    July 28, 2010 | Unregistered Commenterp90x dvds

    PostPost a New Comment

    Enter your information below to add a new comment.

    My response is on my own website »
    Author Email (optional):
    Author URL (optional):
    Post:
     
    Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
    « iPhone Web Apps: monitter.com | Main | jQuery + Twitter + a night of coding »