Maximum Throughput
						(Baseline costs of web frameworks)
						
						Brian McDonnell, CTO at PageFair.com
						
						
					 
				
				
					Typical Production Stack
					
					 
				
				
					Minimum WSGI Implementation
					 
				
				
					Add a Framework to the Mix
					What's the cost before it hits your code?
					 
				
				
					What computation is occurring?
					 
				
				
					
						Anyone with high-frequency, low computation requests
						
							- Analytics (e.g. PageFair)
- Tracking pixels
- Real-time geo-positioning data
 
				
				
					
						Why use a framework?
						Why not?
						
							- Framework should support you when you need it
- And get out of the way when you don't
 
				
				
					Testing Python Web Frameworks
					 
				
				
					Testing Python Async 'Frameworks'
					 
				
				
					Testing Non-python Async 'Frameworks'
					 
				
				
					Socket Types & Protocols
					 
				
				
				
					Measuring Throughput
					
						- Use weighttp to offer load to the server
- Throughput asymptotes at resource exhaustion
- In these tests
							- CPU Exhaustion
- No IO limits hit
- Never out of Memory
Set up 2 AWS Small Instances
					 
					and watch them fight...
				
				
				
					
						Disable non-essential services
						
							- Disable per-request logging
- Disable any debug modes
- Stop all unrequired services (munin, cron jobs)
 
				
				
					
						Lots of requests, sockets, file handles
						
							- Increase file handles
- Increase listen backlogs
- Increase somaxconn & tcp_max_syn_backlog
 
				
				
					
						File Handles
						/etc/security/limits.conf
						
							
*                hard    nofile          131072
*                soft    nofile          65536
							
						
						In terminal for current session
						
							
$ ulimit -Hn 131072
$ ulimit -Sn 65536
							
						
					 
				
				
					
						Socket Backlog
						/etc/sysctl.conf
						
							
net.core.somaxconn = 8191
net.ipv4.tcp_max_syn_backlog = 8191
							
						
						In terminal for current session
						
							
sudo sysctl -w net.core.somaxconn=8191
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=8191
							
						
					 
				
				
					Verify changes
					
					
						
$ cat /proc/sys/fs/file-max
$ ulimit -Hn
$ ulimit -Sn
$ cat /proc/sys/net/ipv4/tcp_max_syn_backlog
$ cat /proc/sys/net/core/somaxconn
						
					
				
				
					First Test
					
						
							 
							
								- Weighttp runs a number of 'clients'
- Each client requests a URL from target server
- Clients wait for a response before issuing next request
 
					 
				
				
					Weighttp Vs Nginx
					
						
							|  | Weighttp | Nginx | 
|---|
						
						
							| Language | C | C | 
|---|
							| Concurrency Model | Single-threaded, evented | Single-threaded, evented | 
|---|
							| Supports | Reduced HTTP | Full HTTP | 
|---|
						
					
				
				
					Using Weighttp
					
						
$ weighttp -c 1 -n 1000 http://server.pycon.ie/hello
						
					
					
						- Increase concurrent clients
- Monitor CPU saturation
- Monitor thoughput asymptoting
						
$ weighttp -c 2 -n 1000 http://server.pycon.ie/hello
...
$ weighttp -c 4 -n 2000 http://server.pycon.ie/hello
...
$ weighttp -c 8 -n 4000 http://server.pycon.ie/hello
... etc...
						
					
				
				
					Nginx Config
					
						
worker_processes 1;
events {
	worker_connections 8191;
}
server {
	listen 80 backlog=8191;
	server_name server.pycon.ie;
	location /hello {
		return 200 'Hello from nginx';
	}
}
						
					
				
				
					Live Demo
					(If we can we SSH to AWS...)
				
				
				
				
					
						
Watch out for 'steel' on AWS
						CPU taken away from a virtual machine to serve other purposes
					
				
				
					Results: Weighttp---|--->Nginx
					
						
							
								|  |  | Concurrent Clients | 
							
								|  |  | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 
						
						
							
								| Nginx | q/s | 459 | 899 | 1747 | 2155 | 2976 | 3434 | 3780 | 3755 | 
						
					
				
				
					Results: Weighttp---|--->Nginx
					 
				
				
					
						User vs System
						How did Nginx saturate the CPU?
						
							
								
									|  | IO | Computation | 
|---|
									|  |  | User | System | 
|---|
								
								
									| TCP Cxn Open | syn-ack, ack |  | accept | 
									| Read request | recv data |  | read | 
									| Parse request | - | string parsing |  | 
									| Match request(nginx.conf) | - | rule matching |  | 
									| Write response | send data |  | send | 
									| TCP Cxn close | fin, fin-ack |  | close | 
								
							
						
					 
				
				
					Test 2: Nginx-->uwsgi-python
					
						- http vs fastcgi vs uwsgi protocols
- tcp vs unix sockets
 
				
				
					uWSGI Listen Backlog
					app-specific xml config
					
						
	0.0.0.0:20000
	1
	1
	8191
						
					
				
				
					Results: Weighttp-->Nginx-->Uwsgi
					Guess?
					
						
							
								|  | Socket | Protocol | AppServer | App | q/s | 
						
						
							| Nginx |  |  |  |  | 3780 | 
							| Nginx | tcp | http | uWSGI | py skel | ? | 
							| Nginx | tcp | fastcgi | uWSGI | py skel | ? | 
							| Nginx | tcp | uwsgi | uWSGI | py skel | ? | 
						
					
					
				
					Results: Weighttp-->Nginx-->Uwsgi
					
						
							
								|  | Socket | Protocol | AppServer | App | q/s | 
						
						
							| Nginx |  |  |  |  | 3780 | 
							| Nginx | tcp | http | uWSGI | py skel | 1247 | 
							| Nginx | tcp | fastcgi | uWSGI | py skel | 1177 | 
							| Nginx | tcp | uwsgi | uWSGI | py skel | 1319 | 
						
					
				
				
					Results: Weighttp-->Nginx-->Uwsgi
					
						
							
								|  | Socket | Protocol | AppServer | App | q/s | 
						
						
							| Nginx |  |  |  |  | 3780 | 
							| Nginx | tcp | http | uWSGI | py skel | 1247 | 
							| Nginx | tcp | fastcgi | uWSGI | py skel | 1177 | 
							| Nginx | tcp | uwsgi | uWSGI | py skel | 1319 | 
							| Nginx | unix | uwsgi | uWSGI | py skel | 1495 | 
						
					
				
				
				
					Test Python Web Frameworks
					 
				
				
					Results: Weighttp-->Nginx-->Uwsgi
					
						
							
								|  | Socket | Protocol | AppServer | App | q/s | 
						
						
							| Nginx |  |  |  |  | 3780 | 
							| Nginx | unix | uwsgi | uWSGI | py skel | 1495 | 
							| Nginx | unix | uwsgi | uWSGI | Django | 321 | 
							| Nginx | unix | uwsgi | uWSGI | Django* | 816 | 
							| Nginx | unix | uwsgi | uWSGI | Flask | 708 | 
							| Nginx | unix | uwsgi | uWSGI | Bottle | 1338 | 
							| Nginx | unix | uwsgi | uWSGI | CherryPy | 417 | 
						
					
					Django* has all default middleware removed.
				
				
				
					Test Python Async Frameworks
					 
				
				
					Results: Weighttp-->Nginx-->Uwsgi
					
						
							
								
									|  | Socket | Protocol | AppServer | App | q/s | 
							
							
								| Nginx |  |  |  |  | 3780 | 
								| Nginx | unix | uwsgi | uWSGI | py skel | 1495 | 
								| Nginx | unix | uwsgi | uWSGI | Bottle | 1338 | 
								| Nginx | unix | uwsgi | uWSGI | Django* | 816 | 
								| Nginx | unix | uwsgi | uWSGI | Flask | 708 | 
								| Nginx | unix | uwsgi | uWSGI | CherryPy | 417 | 
								| Nginx | unix | uwsgi | uWSGI | Django | 321 | 
								 |  | | Nginx | unix | uwsgi | uWSGI | Twisted | 530 | 
								| Nginx | unix | uwsgi | uWSGI | Tornado | 645 | 
							
						
						Django* has all default middleware removed.
					
				
				
				
					Test Non-python Async Frameworks
					 
				
				
				Results: Weighttp-->Nginx-->Uwsgi
					
						
							
								
									|  | Socket | Protocol | AppServer | App | q/s | 
							
							
								| Nginx |  |  |  |  | 3780 | 
								| Nginx | unix | uwsgi | uWSGI | py skel | 1495 | 
								| Nginx | unix | uwsgi | uWSGI | Bottle | 1338 | 
								| Nginx | unix | uwsgi | uWSGI | Django* | 816 | 
								| Nginx | unix | uwsgi | uWSGI | Flask | 708 | 
								| Nginx | unix | uwsgi | uWSGI | Tornado | 645 | 
								| Nginx | unix | uwsgi | uWSGI | Twisted | 530 | 
								| Nginx | unix | uwsgi | uWSGI | CherryPy | 417 | 
								| Nginx | unix | uwsgi | uWSGI | Django | 321 | 
								 |  | | Nginx | unix | uwsgi | uWSGI | Nodejs | 721 | 
								| Nginx | unix | uwsgi | uWSGI | golang | 1305 | 
							
						
						Django* has all default middleware removed.
					
				
				
				
				
				Lastly: all results in desc order
				
				
				Results: Weighttp-->Nginx-->Uwsgi
					
						
							
								
									|  | Socket | Protocol | AppServer | App | q/s | 
							
							
								| Nginx |  |  |  |  | 3780 | 
								| Nginx | unix | uwsgi | uWSGI | py skel app | 1495 | 
								| Nginx | unix | uwsgi | uWSGI | Bottle | 1338 | 
								| Nginx | unix | uwsgi | uWSGI | Golang | 1305 | 
								| Nginx | unix | uwsgi | uWSGI | Django* | 816 | 
								| Nginx | unix | uwsgi | uWSGI | Nodejs | 721 | 
								| Nginx | unix | uwsgi | uWSGI | Flask | 708 | 
								| Nginx | unix | uwsgi | uWSGI | Tornado | 645 | 
								| Nginx | unix | uwsgi | uWSGI | Twisted | 530 | 
								| Nginx | unix | uwsgi | uWSGI | CherryPy | 417 | 
								| Nginx | unix | uwsgi | uWSGI | Django | 321 | 
							
						
						Django* has all default middleware removed.
					
				
				
				
				
					Throughput Vs Performance
					
						- Assuming CPU-bound
- ↓computational-cost == ↑throughput
- ↓io-time =/= ↑throughput
- Time-per-request can be poor, while throughput is great
Doing work in your code...
					
						- Django-min, /hello, 64 clients => 816 q/s
- Django-min, /render_name (simple template render), 64 clients => 689 q/s
- Django-min, /render_table (render 6x4 html table), 64 clients => 223 q/s
- Django-min, /lookup (50,000 lookups into a 1,000 item set), 64 clients => 71 q/s
Themes
					
						Reveal.js comes with a few themes built in: 
						Sky -
						Beige -
						Simple -
						Serif -
						Night -
						Default
					
					
						
							* Theme demos are loaded after the presentation which leads to flicker. In production you should load your theme in the <head> using a <link>.