Posted by mark.
Posted by mark.
How to handle 1000’s of concurrent users on a 360MB VPS
There has been some recent confusion about how much memory you need in a web server to handle a huge number of concurrent requests. I also made a performance claim on the STS list that got me an unusual number of private emails.
Here’s how you run a highly concurrent website on a shoe-string budget:
The first thing you’ll do is get a Linode server because they have the fastest CPU and disk.
Install Apache with your web application running under mod_php, mod_perl or some other persistence engine for your language. Then you get famous and start getting emails about people not being able to access your website.
You increase the number of Apache threads or processes (depending on which Apache MPM you’re using) until you can’t anymore because you only have 360MB of memory in your server.
Then you’ll lower the KeepaliveTimeout and eventually disable Keepalive so that more users can access your website without tying up your Apache processes. Your users will slow down a little because they now have to re-establish a new connection for every piece of your website they want to fetch, but you’ll be able to serve more of them.
But as you scale up you will get a few more emails about your server being down. Even though you’ve disabled keepalive it still takes time for each Apache child to send data to users, especially if they’re on slow connections or connections with high latency. Here’s what you do next:
Install Nginx on your new Linode box and get it to listen on Port 80. Then reconfigure Apache so that it listens on another port – say port 81 – and can only be accessed from the local machine. Configure Nginx as a reverse proxy to Apache listening on port 81 so that it sits in front of Apache like so:
YourVisitor <—–> Nginx:Port80 <—–> Apache:Port81
Enable Keepalive on Nginx and set the Keepalive timeout as high as you’d like. Disable Keepalive on Apache – this is just-in-case because Nginx’s proxy engine doesn’t support Keepalive to the back-end servers anyway.
The 10 or so Apache children you’re running will be getting requests from a client (Nginx) that is running locally. Because there is zero latency and a huge amount of bandwidth (it’s a loopback request), the only time Apache takes to handle the request is the amount of CPU time it actually takes to handle the request. Apache children are no longer tied up with clients on slow connections. So each request is handled in a few microseconds, freeing up each child to do a hell of a lot more work.
Nginx will occupy about 5 to 10 Megs of Memory. You’ll see thousands of users concurrently connected to it. If you have Munin loaded on your server check out the netstat graph. Bitchin isn’t it? You’ll also notice that Nginx uses very little CPU – almost nothing in fact. That’s because Nginx is designed using a single threaded model where one thread handles a huge number of connections. It can do this with little CPU usage because it uses a feature in the Linux kernel called epoll().
Footnotes:
Lack of time forced me to leave out all explanations on how to install and configure Nginx (I’m assuming you know Apache already) – but the Nginx Wiki is excellent, even if the Russain translation is a little rough.
I’ve also purposely left out all references to solving disk bottlenecks (as I’ve left out a discussion about browser caching) because there has been a lot written about this and depending on what app or app-server you’re running, there are some very standard ways to solve IO problems already. e.g. Memcached, the InnoDB cache for MySQL, PHP’s Alternative PHP Cache, perstence engines that keep your compiled code in memory, etc..etc..
This technique works to speed up any back-end application server that uses a one-thread-per-connection model. It doesn’t matter if it’s Ruby via FastCGI, Mod_Perl on Apache or some crappy little Bash script spitting out data on a socket.
This is a very standard config for most high traffic websites today. It’s how they are able to leave keepalive enabled and handle a huge number of concurrent users with a relatively small app server cluster. Lighttpd and Nginx are the two most popular free FSM/epoll web servers out there and Nginx is the fastest growing, best designed (IMHO) and the one I use to serve 400 requests per second on a small Apache cluster. It’s also what guys like Wordpress.com use.
lighttpd as a front end to a fastcgi app is the same basic design. You’re putting a finite-state-machine designed web server that uses a single thread and can handle a huge number of concurrent connections in front of a one-thread-per-connection application server.
Lighttpd vs Nginx is just personal preference. I haven’t used Lighttpd enough to give you a good opinion on it but Nginx’s configuration file is very flexible, it has some great modules, it can also run as multiple processes so that you can use all your CPU cores on a heavily loaded site. I’ve been using Nginx in production and I regularly handle over 30,000 concurrent connections daily with nginx with hardly any server load (reverse proxying to apache).
I recently setup a VPS using Apache and have been load testing it to see what it’s capable of. I knew about Nginx and using it as a reverse proxy to Apache, but I didn’t know that it provided such a huge performance benefit.
Truly great article. Thanks
One question though: Have you tested this configuration with Comet services at all? I suspect that it should work since, but just wondering if you had any first hand experiences.
Thanks again
Why not just set up the server to run on Nginx from the start instead of bloating things with apache? You can easily get Nginx to spawn more worker processes. Our blog easily handles 10k+ Visits/day on a 360mb Linode without ever using more than 5% CPU
« Fly Fishing on lake Samm Next Post
Crowdsourcing a real-time solution to air terrorism »
This may be a naive question: what is the advantage of this (and I know it is widely used) over, for example, Lighttpd with Fast CGI?