├── gen-ssl.sh ├── .gitignore ├── public └── index.html ├── nginx.conf ├── nginx-node.conf ├── nginx-gzip-node.conf ├── nginx-cluster-node.conf ├── nginx-ssl.conf ├── nginx-ssl-node.conf ├── nginx-node.js ├── node.js ├── node-gzip.js ├── node-ssl.js ├── node-cluster.js └── README.md /gen-ssl.sh: -------------------------------------------------------------------------------- 1 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ssl.key -out ssl.crt 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | ssl.crt 4 | ssl.key 5 | 6 | client_body_temp 7 | fastcgi_temp 8 | logs 9 | proxy_temp 10 | scgi_temp 11 | uwsgi_temp 12 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello World! 5 | 6 | 7 |

Hello World!

8 | 9 | 10 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | error_log /dev/null; 2 | daemon off; 3 | worker_processes 1; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | access_log /dev/null; 11 | server { 12 | listen 8080; 13 | server_name localhost; 14 | 15 | location / { 16 | root ./public; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nginx-node.conf: -------------------------------------------------------------------------------- 1 | error_log /dev/null; 2 | daemon off; 3 | worker_processes 1; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | access_log /dev/null; 11 | server { 12 | listen 8080; 13 | server_name localhost; 14 | 15 | location / { 16 | proxy_pass http://localhost:8081; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nginx-gzip-node.conf: -------------------------------------------------------------------------------- 1 | error_log /dev/null; 2 | daemon off; 3 | worker_processes 1; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | access_log /dev/null; 11 | server { 12 | gzip on; 13 | listen 8080; 14 | server_name localhost; 15 | 16 | location / { 17 | proxy_pass http://localhost:8081; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /nginx-cluster-node.conf: -------------------------------------------------------------------------------- 1 | error_log /dev/null; 2 | daemon off; 3 | worker_processes 1; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | access_log /dev/null; 11 | 12 | upstream nodeserver { 13 | server localhost:8081; 14 | server localhost:8082; 15 | } 16 | 17 | server { 18 | listen 8080; 19 | 20 | location / { 21 | proxy_pass http://nodeserver; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /nginx-ssl.conf: -------------------------------------------------------------------------------- 1 | error_log /dev/null; 2 | daemon off; 3 | worker_processes 1; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | access_log /dev/null; 11 | server { 12 | # listen 8080; 13 | listen 8443 ssl; 14 | ssl_certificate ./ssl.crt; 15 | ssl_certificate_key ./ssl.key; 16 | 17 | server_name localhost; 18 | 19 | location / { 20 | root ./public; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nginx-ssl-node.conf: -------------------------------------------------------------------------------- 1 | error_log /dev/null; 2 | daemon off; 3 | worker_processes 1; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | access_log /dev/null; 11 | server { 12 | # listen 8080; 13 | listen 8443 ssl; 14 | ssl_certificate ./ssl.crt; 15 | ssl_certificate_key ./ssl.key; 16 | 17 | server_name localhost; 18 | 19 | location / { 20 | proxy_pass http://localhost:8081; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nginx-node.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const http = require('http'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | 7 | const server = http.createServer((request, response) => { 8 | const file = path.join(__dirname, 'public', 'index.html'); 9 | fs.stat(file, (err, stat) => { 10 | 11 | response.writeHead(200, { 12 | 'Content-Type': 'text/html', 13 | 'Content-Length': stat.size 14 | }); 15 | 16 | const readStream = fs.createReadStream(file); 17 | 18 | readStream.pipe(response); 19 | }); 20 | }); 21 | 22 | server.listen(8081); 23 | 24 | console.log(process.pid); 25 | -------------------------------------------------------------------------------- /node.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const http = require('http'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const port = process.argv[2] || 8080; 7 | 8 | const server = http.createServer((request, response) => { 9 | const file = path.join(__dirname, 'public', 'index.html'); 10 | fs.stat(file, (err, stat) => { 11 | 12 | response.writeHead(200, { 13 | 'Content-Type': 'text/html', 14 | 'Content-Length': stat.size 15 | }); 16 | 17 | const readStream = fs.createReadStream(file); 18 | 19 | readStream.pipe(response); 20 | }); 21 | }); 22 | 23 | server.listen(port); 24 | 25 | console.log('PORT', port, 'PID', process.pid); 26 | -------------------------------------------------------------------------------- /node-gzip.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const http = require('http'); 4 | const fs = require('fs'); 5 | const zlib = require('zlib'); 6 | const path = require('path'); 7 | const port = process.argv[2] || 8080; 8 | 9 | const server = http.createServer((request, response) => { 10 | const file = path.join(__dirname, 'public', 'index.html'); 11 | fs.stat(file, (err, stat) => { 12 | 13 | response.writeHead(200, { 'Content-Encoding': 'gzip' }); 14 | 15 | const readStream = fs.createReadStream(file); 16 | 17 | readStream.pipe(zlib.createGzip()).pipe(response); 18 | }); 19 | }); 20 | 21 | server.listen(port); 22 | 23 | console.log('PORT', port, 'PID', process.pid); 24 | -------------------------------------------------------------------------------- /node-ssl.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const https = require('https'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | 7 | const options = { 8 | key: fs.readFileSync('./ssl.key'), 9 | cert: fs.readFileSync('./ssl.crt'), 10 | }; 11 | 12 | const server = https.createServer(options, (request, response) => { 13 | const file = path.join(__dirname, 'public', 'index.html'); 14 | fs.stat(file, (err, stat) => { 15 | 16 | response.writeHead(200, { 17 | 'Content-Type': 'text/html', 18 | 'Content-Length': stat.size 19 | }); 20 | 21 | const readStream = fs.createReadStream(file); 22 | 23 | readStream.pipe(response); 24 | }); 25 | }); 26 | 27 | server.listen(8443); 28 | 29 | console.log(process.pid); 30 | -------------------------------------------------------------------------------- /node-cluster.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const http = require('http'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const cluster = require('cluster'); 7 | const SLAVES = 2; 8 | 9 | if (cluster.isMaster) { 10 | console.log(`Master ${process.pid} is running`); 11 | 12 | // Fork workers. 13 | for (let i = 0; i < SLAVES; i++) { 14 | cluster.fork(); 15 | } 16 | 17 | cluster.on('exit', (worker, code, signal) => { 18 | console.log(`worker ${worker.process.pid} died`); 19 | }); 20 | } else { 21 | const server = http.createServer((request, response) => { 22 | const file = path.join(__dirname, 'public', 'index.html'); 23 | fs.stat(file, (err, stat) => { 24 | 25 | response.writeHead(200, { 26 | 'Content-Type': 'text/html', 27 | 'Content-Length': stat.size 28 | }); 29 | 30 | const readStream = fs.createReadStream(file); 31 | 32 | readStream.pipe(response); 33 | }); 34 | }); 35 | 36 | server.listen(8080); 37 | 38 | console.log(process.pid); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nodejs-reverse-proxy-benchmarks 2 | 3 | This is a companion repository for our blog post, "Why should I use a Reverse 4 | Proxy if Node.js is Production-Ready?" 5 | 6 | You will probably want to download a recent stable version of 7 | [Nginx](https://nginx.org/en/download.html) to perform these benchmarks. You'll 8 | also want a recent stable version of [Node.js](https://nodejs.org/en/download/) 9 | as well. 10 | 11 | If you compile Nginx from source make sure you provide the 12 | `--with-http_ssl_module` flag, otherwise the SSL tests will fail. 13 | 14 | These tests currently use a single Nginx worker thread via the 15 | `worker_processes 1;` directive. Running them again with 2 workers would yield 16 | faster Nginx performance at a trade-off of more memory usage. 17 | 18 | The HTTP benchmarks used for the article are done with the 19 | [siege](https://github.com/JoeDog/siege) tool, though any HTTP benchmarking 20 | tool should work fine. 21 | 22 | ## Our Results 23 | 24 | | | req/sec | approx memory | 25 | |--------------------|---------|---------------| 26 | | nginx | 25,445 | 46.1MB | 27 | | nginx-ssl | 959 | 46.4MB | 28 | | node | 9,881 | 601MB | 29 | | node-ssl | 746 | 614MB | 30 | | nginx-node | 8,117 | 652MB | 31 | | nginx-ssl-node | 865 | 652MB | 32 | | node-cluster | 8,006 | 1,768MB | 33 | | nginx-cluster-node | 7,908 | 1,253MB | 34 | | node-gzip | 5,047 | 598MB | 35 | | nginx-gzip-node | 7,590 | 652MB | 36 | 37 | ## Running the Benchmarks 38 | 39 | The following sections contain instruction for recreating each of the benchmark 40 | results: 41 | 42 | ### `nginx` 43 | 44 | ```bash 45 | nginx -c nginx.conf -p . 46 | siege -c 10 -r 20000 -b http://localhost:8080/ 47 | ``` 48 | 49 | ### `nginx-ssl` 50 | 51 | ```bash 52 | ./gen-ssl.sh # only needs to be run once 53 | nginx -c nginx-ssl.conf -p . 54 | siege -c 10 -r 20000 -b https://localhost:8443/ 55 | ``` 56 | 57 | ### `node` 58 | 59 | ```bash 60 | ./node.js 8080 61 | siege -c 10 -r 20000 -b http://localhost:8080/ 62 | ``` 63 | 64 | ### `node-ssl` 65 | 66 | ```bash 67 | ./gen-ssl.sh # only needs to be run once 68 | ./node-ssl.js 69 | siege -c 10 -r 20000 -b https://localhost:8443/ 70 | ``` 71 | 72 | ### `nginx-node` 73 | 74 | ```bash 75 | ./node.js 8081 76 | nginx -c nginx-node.conf -p . 77 | siege -c 10 -r 20000 -b http://localhost:8080/ 78 | ``` 79 | 80 | ### `nginx-ssl-node` 81 | 82 | ```bash 83 | ./gen-ssl.sh # only needs to be run once 84 | ./node.js 8081 85 | nginx -c nginx-ssl-node.conf -p . 86 | siege -c 10 -r 20000 -b https://localhost:8443/ 87 | ``` 88 | 89 | ### `node-cluster` 90 | 91 | ```bash 92 | ./node-cluster.js 93 | siege -c 10 -r 20000 -b http://localhost:8080/ 94 | ``` 95 | 96 | ### `nginx-cluster-node` 97 | 98 | ```bash 99 | ./node.js 8081 100 | ./node.js 8082 101 | nginx -c nginx-cluster-node.conf -p . 102 | siege -c 10 -r 20000 -b http://localhost:8080/ 103 | ``` 104 | 105 | ### `node-gzip` 106 | 107 | ```bash 108 | ./node-gzip.js 109 | siege -c 10 -r 20000 -b http://localhost:8080/ 110 | ``` 111 | 112 | ### `nginx-gzip-node` 113 | 114 | ```bash 115 | ./node.js 8081 116 | nginx -c nginx-gzip-node.conf -p . 117 | siege -c 10 -r 20000 -b http://localhost:8080/ 118 | ``` 119 | --------------------------------------------------------------------------------