Thursday, August 28, 2008 |
21 Comments | 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 ;)




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
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.
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 ;)
excellent solution....
Thanks for sharing.
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!!
Good site you have here :)
Looking for proxy to unblock sites?
Try this one http://.4ever3.info
Hope it helps :)
cheers!
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.
wallets
men wallet
travel wallet
credit card wallet
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
wholesale louis vuitton
chanel handbags
prada handbags
louis vuitton bags
louis vuitton wallets
louis vuitton shop
louis vuitton bags
prada
christian louboutin shoes
chanel purses
louis vuitton
balenciaga
louis vuitton
louis vuitton shoes
Nice tips.
seo agency
we like coach 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!
shrek forever after download
download iron man 2
letters to juliet download
movie downloads
salt download
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!
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.
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.
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
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.
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.