├── 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 |
--------------------------------------------------------------------------------