SlideShare a Scribd company logo
NODE.JS SCALABILITY TIPS
NODE.JS SCALABILITY TIPS
Luciano Mammino ( )
@loige
loige.link/node-adc
Vijayawada - 2021-12-13
1
@loige
Get the slides! 👇
loige.link/node-adc
2
HELLO!
3
HELLO!
👋I'm Luciano ( 🍕🍝 )
3
HELLO!
👋I'm Luciano ( 🍕🍝 )
Senior Architect @ fourTheorem (Dublin )
3
HELLO!
👋I'm Luciano ( 🍕🍝 )
Senior Architect @ fourTheorem (Dublin )
nodejsdp.link
📔Co-Author of Node.js Design Patterns  👉
3
HELLO!
👋I'm Luciano ( 🍕🍝 )
Senior Architect @ fourTheorem (Dublin )
nodejsdp.link
📔Co-Author of Node.js Design Patterns  👉
Let's connect!
  (blog)
  (twitter)
  (twitch)
  (github)
loige.co
@loige
loige
lmammino
3
WE ARE BUSINESS FOCUSED TECHNOLOGISTS THAT DELIVER.
 |  |
Accelerated Serverless AI as a Service Platform Modernisation
We are hiring: do you want to ?
work with us
loige 4
"A service is said to be scalable if when we
increase the resources in a system, it
results in increased performance in a
manner proportional to resources added"
— Werner Vogels
@loige
5
🛫
TIP 1.
ESTABLISH A BASELINE
@loige
6
/?data=ciao👋
@loige
Site:  - Code:
qrgen.lmammino.repl.co replit.com/@lmammino/QRGen 7
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@loige
8
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createServer } from 'http'
import QRCode from 'qrcode'
1
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
@loige
8
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createServer } from 'http'
import QRCode from 'qrcode'
1
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
createServer(function handler (req, res) {
}).listen(8080)
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
18
@loige
8
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createServer } from 'http'
import QRCode from 'qrcode'
1
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
createServer(function handler (req, res) {
}).listen(8080)
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
18
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
5
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
@loige
8
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createServer } from 'http'
import QRCode from 'qrcode'
1
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
createServer(function handler (req, res) {
}).listen(8080)
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
18
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
5
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
7
8
9
10
11
12
13
14
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
@loige
8
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createServer } from 'http'
import QRCode from 'qrcode'
1
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
createServer(function handler (req, res) {
}).listen(8080)
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
18
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
5
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
7
8
9
10
11
12
13
14
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
16
17
}).listen(8080)
18
@loige
8
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createServer } from 'http'
import QRCode from 'qrcode'
1
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
createServer(function handler (req, res) {
}).listen(8080)
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
18
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
5
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
7
8
9
10
11
12
13
14
15
res.writeHead(200, { 'Content-Type': 'image/png' })
16
QRCode.toFileStream(res, data, { width: 300 })
17
}).listen(8080)
18
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
import { createServer } from 'http'
1
import QRCode from 'qrcode'
2
3
createServer(function handler (req, res) {
4
const url = new URL(req.url, 'http://localhost:8080')
5
const data = url.searchParams.get('data')
6
if (!data) {
7
res.writeHead(200, {'Content-Type': 'text/html'})
8
return res.end(`<html>
9
<body>
10
<p>To use this app you need to set the <code>data</code> querystrin
11
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
12
</body>
13
</html>`)
14
}
15
16
17
}).listen(8080)
18
import { createServer } from 'http'
import QRCode from 'qrcode'
createServer(function handler (req, res) {
const url = new URL(req.url, 'http://localhost:8080')
const data = url.searchParams.get('data')
if (!data) {
res.writeHead(200, {'Content-Type': 'text/html'})
return res.end(`<html>
<body>
<p>To use this app you need to set the <code>data</code> querystrin
<p>Try for example <a href="/?data=hello">/?data=hello</a></p>
</body>
</html>`)
}
res.writeHead(200, { 'Content-Type': 'image/png' })
QRCode.toFileStream(res, data, { width: 300 })
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@loige
8
autocannon -c 200 --on-port / -- node server.js
WRK
node server.js&
wrk -t8 -c200 -d10s http://localhost:8080/
@loige
9
autocannon -c 200 --on-port /?data=ciao👋 -- node server.js
Running 10s test @ http://localhost:8080/?data=ciao👋
200 connections
┌─────────┬─────────┬─────────┬─────────┬─────────┬────────────┬─────────┬────────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼─────────┼─────────┼─────────┼─────────┼────────────┼─────────┼────────────┤
│ Latency │ 1899 ms │ 1951 ms │ 2053 ms │ 2054 ms │ 1964.92 ms │ 99.9 ms │ 3364.03 ms │
└─────────┴─────────┴─────────┴─────────┴─────────┴────────────┴─────────┴────────────┘
┌───────────┬─────┬──────┬─────────┬────────┬────────┬────────┬─────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤
│ Req/Sec │ 0 │ 0 │ 30 │ 199 │ 99.5 │ 94.27 │ 30 │
├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤
│ Bytes/Sec │ 0 B │ 0 B │ 50.7 kB │ 336 kB │ 168 kB │ 159 kB │ 50.7 kB │
└───────────┴─────┴──────┴─────────┴────────┴────────┴────────┴─────────┘
Req/Bytes counts sampled once per second.
995 requests in 10.08s, 1.68 MB read
@loige
10
autocannon -c 200 --on-port /?data=ciao👋 -- node server.js
Running 10s test @ http://localhost:8080/?data=ciao👋
200 connections
┌─────────┬─────────┬─────────┬─────────┬─────────┬────────────┬─────────┬────────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼─────────┼─────────┼─────────┼─────────┼────────────┼─────────┼────────────┤
│ Latency │ 1899 ms │ 1951 ms │ 2053 ms │ 2054 ms │ 1964.92 ms │ 99.9 ms │ 3364.03 ms │
└─────────┴─────────┴─────────┴─────────┴─────────┴────────────┴─────────┴────────────┘
┌───────────┬─────┬──────┬─────────┬────────┬────────┬────────┬─────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤
│ Req/Sec │ 0 │ 0 │ 30 │ 199 │ 99.5 │ 94.27 │ 30 │
├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤
│ Bytes/Sec │ 0 B │ 0 B │ 50.7 kB │ 336 kB │ 168 kB │ 159 kB │ 50.7 kB │
└───────────┴─────┴──────┴─────────┴────────┴────────┴────────┴─────────┘
Req/Bytes counts sampled once per second.
995 requests in 10.08s, 1.68 MB read
@loige
10
⛅
TIP 1-BIS
ALSO, FIND OUT YOUR CEILING
@loige
11
import { createServer } from 'http'
createServer((req, res) => {
if (req.method === 'GET' && req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('Hello Worldn')
} else {
res.statusCode = 404
res.end()
}
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
@loige
12
import { createServer } from 'http'
createServer((req, res) => {
if (req.method === 'GET' && req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('Hello Worldn')
} else {
res.statusCode = 404
res.end()
}
}).listen(8080)
1
2
3
4
5
6
7
8
9
10
11
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('Hello Worldn')
import { createServer } from 'http'
1
2
createServer((req, res) => {
3
if (req.method === 'GET' && req.url === '/') {
4
5
6
} else {
7
res.statusCode = 404
8
res.end()
9
}
10
}).listen(8080)
11
@loige
12
autocannon -c 200 --on-port / -- node server.js
Running 10s test @ http://localhost:8080/
200 connections
┌─────────┬──────┬──────┬───────┬───────┬─────────┬─────────┬──────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼──────┼──────┼───────┼───────┼─────────┼─────────┼──────────┤
│ Latency │ 3 ms │ 5 ms │ 11 ms │ 14 ms │ 5.51 ms │ 2.71 ms │ 80.63 ms │
└─────────┴──────┴──────┴───────┴───────┴─────────┴─────────┴──────────┘
┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬─────────┬─────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
│ Req/Sec │ 21087 │ 21087 │ 34623 │ 35487 │ 33258.4 │ 4107.01 │ 21077 │
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
│ Bytes/Sec │ 3.29 MB │ 3.29 MB │ 5.4 MB │ 5.54 MB │ 5.19 MB │ 641 kB │ 3.29 MB │
└───────────┴─────────┴─────────┴────────┴─────────┴─────────┴─────────┴─────────┘
Req/Bytes counts sampled once per second.
333k requests in 10.1s, 51.9 MB read
@loige
13
autocannon -c 200 --on-port / -- node server.js
Running 10s test @ http://localhost:8080/
200 connections
┌─────────┬──────┬──────┬───────┬───────┬─────────┬─────────┬──────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼──────┼──────┼───────┼───────┼─────────┼─────────┼──────────┤
│ Latency │ 3 ms │ 5 ms │ 11 ms │ 14 ms │ 5.51 ms │ 2.71 ms │ 80.63 ms │
└─────────┴──────┴──────┴───────┴───────┴─────────┴─────────┴──────────┘
┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬─────────┬─────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
│ Req/Sec │ 21087 │ 21087 │ 34623 │ 35487 │ 33258.4 │ 4107.01 │ 21077 │
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
│ Bytes/Sec │ 3.29 MB │ 3.29 MB │ 5.4 MB │ 5.54 MB │ 5.19 MB │ 641 kB │ 3.29 MB │
└───────────┴─────────┴─────────┴────────┴─────────┴─────────┴─────────┴─────────┘
Req/Bytes counts sampled once per second.
333k requests in 10.1s, 51.9 MB read
@loige
13
🍾
TIP 2.
FIND YOUR BOTTLENECK
@loige
14
Clinic.js
clinicjs.org
@loige
15
clinic doctor --autocannon [ -c 200 '/?data=ciao👋' ] -- node server.js
@loige
16
clinic flame --autocannon [ -c 200 '/?data=ciao👋' ] -- node server.js
@loige
17
clinic bubble --autocannon [ -c 200 '/?data=ciao👋' ] -- node server.js
@loige
18
🎳
TIP 3.
UNDERSTAND YOUR GOALS
@loige
19
WHAT DO WE OPTIMIZE FOR?
THROUGHPUT?
MEMORY?
LATENCY?
@loige
20
👁
TIP 4.
ALWAYS "OBSERVE"
@loige
21
I MEAN, IN PRODUCTION!
Logs - Metrics - Traces
@loige
22
🚀
TIP 5.
SCALE YOUR ARCHITECTURE
@loige
23
PERFORMANCE != SCALABILITY
@loige
24
HOW CAN WE SCALE A SYSTEM
BY ADDING RESOURCES?
@loige
25
THE " "
SCALE CUBE
x-axis
cloning
z-axis
partitioning
y-axis
functional
decomposition
@loige
26
THE " "
SCALE CUBE
x-axis
cloning
z-axis
partitioning
y-axis
functional
decomposition
@loige
26
27
Reverse proxy
CLONING
Inside the same server
Load Balancer
Using multiple server
@loige
THE MODULE
CLUSTER
Master process
Worker
process
Worker
process
Worker
process
28
@loige
THE MODULE
CLUSTER
Master process
Worker
process
Worker
process
Worker
process
28
@loige
THE MODULE
CLUSTER
Master process
Worker
process
Worker
process
Worker
process
28
@loige
THE MODULE
CLUSTER
Master process
Worker
process
Worker
process
Worker
process
28
@loige
THE MODULE
CLUSTER
Master process
Worker
process
Worker
process
Worker
process
28
@loige
THE MODULE
CLUSTER
Master process
Worker
process
Worker
process
Worker
process
28
@loige
THE MODULE
CLUSTER
Master process
Worker
process
Worker
process
Worker
process
28
@loige
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
@loige
29
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
3-4x req/sec
(8 core)
@loige
29
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
import { cpus } from 'os'
const numCPUs = cpus().length
import cluster from 'cluster'
1
2
3
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
3-4x req/sec
(8 core)
@loige
29
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
import { cpus } from 'os'
const numCPUs = cpus().length
import cluster from 'cluster'
1
2
3
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
if (cluster.isMaster) {
} else {
}
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
11
// Worker code here...
12
13
3-4x req/sec
(8 core)
@loige
29
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
import { cpus } from 'os'
const numCPUs = cpus().length
import cluster from 'cluster'
1
2
3
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
if (cluster.isMaster) {
} else {
}
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
11
// Worker code here...
12
13
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
7
8
9
10
} else {
11
// Worker code here...
12
}
13
3-4x req/sec
(8 core)
@loige
29
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
import { cpus } from 'os'
const numCPUs = cpus().length
import cluster from 'cluster'
1
2
3
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
if (cluster.isMaster) {
} else {
}
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
11
// Worker code here...
12
13
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
7
8
9
10
} else {
11
// Worker code here...
12
}
13
// Worker code here...
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
12
}
13
3-4x req/sec
(8 core)
@loige
29
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
import { cpus } from 'os'
const numCPUs = cpus().length
import cluster from 'cluster'
1
2
3
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
// Worker code here...
12
}
13
if (cluster.isMaster) {
} else {
}
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
11
// Worker code here...
12
13
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
7
8
9
10
} else {
11
// Worker code here...
12
}
13
// Worker code here...
import cluster from 'cluster'
1
import { cpus } from 'os'
2
3
const numCPUs = cpus().length
4
5
if (cluster.isMaster) {
6
// Fork workers
7
for (let i = 0; i < numCPUs; i++) {
8
cluster.fork()
9
}
10
} else {
11
12
}
13
import cluster from 'cluster'
import { cpus } from 'os'
const numCPUs = cpus().length
if (cluster.isMaster) {
// Fork workers
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Worker code here...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
3-4x req/sec
(8 core)
@loige
29
YOU COULD ALSO USE
CHECK OUT !
WORKER
THREADS
PISCINA
@loige
30
CLONING IS THE EASIEST STRATEGY TO SCALE A SERVICE...
 
... AS LONG AS YOUR APPLICATION IS "STATELESS"
@loige
31
API Gateway
FUNCTIONAL DECOMPOSITION
a.k.a. "Micro-services"
32
/products
/cart
cart DB
products DB
@loige
API Gateway
FUNCTIONAL DECOMPOSITION
a.k.a. "Micro-services"
33
/products
/cart
Functional decomposition can
also be combined with cloning!
cart DB
products DB
@loige
NODE.JS IS GREAT FOR MICROSERVICES
@loige
34
MICROSERVICES CAN ALSO HELP WITH
SCALING THE ORGANISATION!
@loige
35
MICROSERVICES ADD COMPLEXITY
Observability
Deployments
Versioning
Integration
@loige
36
PARTITIONING
Service and Data Partitioning along Customer Boundaries
Shard partitioning
/products/[A-L]/
/products/[M-Z]/
DB 2
37
DB 1
@loige
PARTITIONING IS GENERALLY USED
TO SCALE DATABASES
AND
SAAS SOFTWARE GEOGRAPHICALLY
@loige
38
SUMMARY
@loige
39
SUMMARY
🛫Establish a baseline
@loige
39
SUMMARY
🛫Establish a baseline
🍾Find your bottleneck
@loige
39
SUMMARY
🛫Establish a baseline
🍾Find your bottleneck
🎳Understand your goals
@loige
39
SUMMARY
🛫Establish a baseline
🍾Find your bottleneck
🎳Understand your goals
👁Always "observe"
@loige
39
SUMMARY
🛫Establish a baseline
🍾Find your bottleneck
🎳Understand your goals
👁Always "observe"
🚀Scale your architecture
(cloning, decomposition & partitioning)
@loige
39
☝nodejsdp.link
THANK YOU!
Special thanks to , ,
, , ,
, , ,
, , , ,
, , , ,
, , , ,
,
Icons and SVGs by
Photo by on
@StefanoAbalsamo @matteocollina
@dagonzago @NullishCoalesce @DublinSvelte
@KViglucci @gjohnson391 @lucamaraschi
@laurekamalandua @giltayar @mrm8488 @adrirai
@harafise @EugeneWare @Jauny @tlivings
@michaelcfine @leojino @shahidontech @Lordoomer
@zsadigov @dottorblaster
freepik.com
Jonas Kaiser Unsplash
@loige
loige.link/node-adc
40

More Related Content

What's hot (20)

PPTX
Python Code Camp for Professionals 3/4
DEVCON
 
PPTX
REST with Eve and Python
PiXeL16
 
PPTX
Python Code Camp for Professionals 1/4
DEVCON
 
PDF
Implementing Server Side Data Synchronization for Mobile Apps
Michele Orselli
 
PPTX
Python Code Camp for Professionals 2/4
DEVCON
 
PDF
Apache spark with akka couchbase code by bhawani
Bhawani N Prasad
 
ODP
An Overview of Node.js
Ayush Mishra
 
PDF
Data models in Angular 1 & 2
Adam Klein
 
PDF
Server side data sync for mobile apps with silex
Michele Orselli
 
PDF
Implementing data sync apis for mibile apps @cloudconf
Michele Orselli
 
PDF
Dart Power Tools
Matt Norris
 
ODP
Introduction to Angular js
Mustafa Gamal
 
PDF
Elasticsearch intro output
Tom Chen
 
PDF
Node.js and Parse
Nicholas McClay
 
PPTX
Angular Tutorial Freshers and Experienced
rajkamaltibacademy
 
PDF
The Ring programming language version 1.5.2 book - Part 44 of 181
Mahmoud Samir Fayed
 
PDF
The Big Picture and How to Get Started
guest1af57e
 
PPTX
Progressive What Apps?
Patrick Kettner
 
PPT
Pascarello_Investigating JavaScript and Ajax Security
amiable_indian
 
PDF
Working Effectively with Legacy Javascript code in Opal
Forrest Chang
 
Python Code Camp for Professionals 3/4
DEVCON
 
REST with Eve and Python
PiXeL16
 
Python Code Camp for Professionals 1/4
DEVCON
 
Implementing Server Side Data Synchronization for Mobile Apps
Michele Orselli
 
Python Code Camp for Professionals 2/4
DEVCON
 
Apache spark with akka couchbase code by bhawani
Bhawani N Prasad
 
An Overview of Node.js
Ayush Mishra
 
Data models in Angular 1 & 2
Adam Klein
 
Server side data sync for mobile apps with silex
Michele Orselli
 
Implementing data sync apis for mibile apps @cloudconf
Michele Orselli
 
Dart Power Tools
Matt Norris
 
Introduction to Angular js
Mustafa Gamal
 
Elasticsearch intro output
Tom Chen
 
Node.js and Parse
Nicholas McClay
 
Angular Tutorial Freshers and Experienced
rajkamaltibacademy
 
The Ring programming language version 1.5.2 book - Part 44 of 181
Mahmoud Samir Fayed
 
The Big Picture and How to Get Started
guest1af57e
 
Progressive What Apps?
Patrick Kettner
 
Pascarello_Investigating JavaScript and Ajax Security
amiable_indian
 
Working Effectively with Legacy Javascript code in Opal
Forrest Chang
 

Similar to Node.js: scalability tips - Azure Dev Community Vijayawada (20)

PDF
Node.js server-side rendering
The Software House
 
PPTX
Nodejs.meetup
Vivian S. Zhang
 
PPTX
Developing web-apps like it's 2013
Laurent_VB
 
PDF
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
PDF
Micro app-framework - NodeLive Boston
Michael Dawson
 
PDF
Micro app-framework
Michael Dawson
 
PDF
Angular server side rendering - Strategies & Technics
Eliran Eliassy
 
PDF
Engage 2023: Taking Domino Apps to the next level by providing a Rest API
Serdar Basegmez
 
DOCX
bbyopenApp_Code.DS_StorebbyopenApp_CodeVBCodeGoogleMaps.docx
ikirkton
 
PDF
Is html5-ready-workshop-110727181512-phpapp02
PL dream
 
PDF
Is HTML5 Ready? (workshop)
Remy Sharp
 
PDF
HTML5: huh, what is it good for?
Remy Sharp
 
PPT
Enhance Web Performance
Adam Lu
 
PDF
A mobile web app for Android in 75 minutes
James Pearce
 
PDF
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Fwdays
 
PDF
Introduction to Node.js
Somkiat Puisungnoen
 
PDF
The Future of CSS with Web components
devObjective
 
PDF
The Future of CSS with Web Components
ColdFusionConference
 
PDF
DataStax: 0 to App faster with Ruby and NodeJS
DataStax Academy
 
PDF
Having Fun with Play
Clinton Dreisbach
 
Node.js server-side rendering
The Software House
 
Nodejs.meetup
Vivian S. Zhang
 
Developing web-apps like it's 2013
Laurent_VB
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
Micro app-framework - NodeLive Boston
Michael Dawson
 
Micro app-framework
Michael Dawson
 
Angular server side rendering - Strategies & Technics
Eliran Eliassy
 
Engage 2023: Taking Domino Apps to the next level by providing a Rest API
Serdar Basegmez
 
bbyopenApp_Code.DS_StorebbyopenApp_CodeVBCodeGoogleMaps.docx
ikirkton
 
Is html5-ready-workshop-110727181512-phpapp02
PL dream
 
Is HTML5 Ready? (workshop)
Remy Sharp
 
HTML5: huh, what is it good for?
Remy Sharp
 
Enhance Web Performance
Adam Lu
 
A mobile web app for Android in 75 minutes
James Pearce
 
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Fwdays
 
Introduction to Node.js
Somkiat Puisungnoen
 
The Future of CSS with Web components
devObjective
 
The Future of CSS with Web Components
ColdFusionConference
 
DataStax: 0 to App faster with Ruby and NodeJS
DataStax Academy
 
Having Fun with Play
Clinton Dreisbach
 
Ad

More from Luciano Mammino (20)

PDF
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the ben...
Luciano Mammino
 
PDF
Did you know JavaScript has iterators? DublinJS
Luciano Mammino
 
PDF
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
Luciano Mammino
 
PDF
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Luciano Mammino
 
PDF
From Node.js to Design Patterns - BuildPiper
Luciano Mammino
 
PDF
Let's build a 0-cost invite-only website with Next.js and Airtable!
Luciano Mammino
 
PDF
Everything I know about S3 pre-signed URLs
Luciano Mammino
 
PDF
Serverless for High Performance Computing
Luciano Mammino
 
PDF
Serverless for High Performance Computing
Luciano Mammino
 
PDF
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
Luciano Mammino
 
PDF
Building an invite-only microsite with Next.js & Airtable
Luciano Mammino
 
PDF
Let's take the monolith to the cloud 🚀
Luciano Mammino
 
PDF
A look inside the European Covid Green Certificate - Rust Dublin
Luciano Mammino
 
PDF
Monoliths to the cloud!
Luciano Mammino
 
PDF
The senior dev
Luciano Mammino
 
PDF
A look inside the European Covid Green Certificate (Codemotion 2021)
Luciano Mammino
 
PDF
AWS Observability Made Simple
Luciano Mammino
 
PDF
Semplificare l'observability per progetti Serverless
Luciano Mammino
 
PDF
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Luciano Mammino
 
PDF
Finding a lost song with Node.js and async iterators - EnterJS 2021
Luciano Mammino
 
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the ben...
Luciano Mammino
 
Did you know JavaScript has iterators? DublinJS
Luciano Mammino
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Luciano Mammino
 
From Node.js to Design Patterns - BuildPiper
Luciano Mammino
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Luciano Mammino
 
Everything I know about S3 pre-signed URLs
Luciano Mammino
 
Serverless for High Performance Computing
Luciano Mammino
 
Serverless for High Performance Computing
Luciano Mammino
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable
Luciano Mammino
 
Let's take the monolith to the cloud 🚀
Luciano Mammino
 
A look inside the European Covid Green Certificate - Rust Dublin
Luciano Mammino
 
Monoliths to the cloud!
Luciano Mammino
 
The senior dev
Luciano Mammino
 
A look inside the European Covid Green Certificate (Codemotion 2021)
Luciano Mammino
 
AWS Observability Made Simple
Luciano Mammino
 
Semplificare l'observability per progetti Serverless
Luciano Mammino
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Luciano Mammino
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Luciano Mammino
 
Ad

Recently uploaded (20)

PPTX
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
PDF
Biography of Daniel Podor.pdf
Daniel Podor
 
PDF
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PDF
Advancing WebDriver BiDi support in WebKit
Igalia
 
PDF
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
PDF
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PPTX
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
PDF
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
PPTX
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
PDF
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PDF
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
PPTX
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
PDF
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
Biography of Daniel Podor.pdf
Daniel Podor
 
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
Advancing WebDriver BiDi support in WebKit
Igalia
 
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 

Node.js: scalability tips - Azure Dev Community Vijayawada

  • 1. NODE.JS SCALABILITY TIPS NODE.JS SCALABILITY TIPS Luciano Mammino ( ) @loige loige.link/node-adc Vijayawada - 2021-12-13 1
  • 2. @loige Get the slides! 👇 loige.link/node-adc 2
  • 5. HELLO! 👋I'm Luciano ( 🍕🍝 ) Senior Architect @ fourTheorem (Dublin ) 3
  • 6. HELLO! 👋I'm Luciano ( 🍕🍝 ) Senior Architect @ fourTheorem (Dublin ) nodejsdp.link 📔Co-Author of Node.js Design Patterns  👉 3
  • 7. HELLO! 👋I'm Luciano ( 🍕🍝 ) Senior Architect @ fourTheorem (Dublin ) nodejsdp.link 📔Co-Author of Node.js Design Patterns  👉 Let's connect!   (blog)   (twitter)   (twitch)   (github) loige.co @loige loige lmammino 3
  • 8. WE ARE BUSINESS FOCUSED TECHNOLOGISTS THAT DELIVER.  |  | Accelerated Serverless AI as a Service Platform Modernisation We are hiring: do you want to ? work with us loige 4
  • 9. "A service is said to be scalable if when we increase the resources in a system, it results in increased performance in a manner proportional to resources added" — Werner Vogels @loige 5
  • 10. 🛫 TIP 1. ESTABLISH A BASELINE @loige 6
  • 12. import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @loige 8
  • 13. import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 @loige 8
  • 14. import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 @loige 8
  • 15. import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 @loige 8
  • 16. import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 7 8 9 10 11 12 13 14 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 @loige 8
  • 17. import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 7 8 9 10 11 12 13 14 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 16 17 }).listen(8080) 18 @loige 8
  • 18. import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 7 8 9 10 11 12 13 14 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(`<html> 9 <body> 10 <p>To use this app you need to set the <code>data</code> querystrin 11 <p>Try for example <a href="/?data=hello">/?data=hello</a></p> 12 </body> 13 </html>`) 14 } 15 16 17 }).listen(8080) 18 import { createServer } from 'http' import QRCode from 'qrcode' createServer(function handler (req, res) { const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`<html> <body> <p>To use this app you need to set the <code>data</code> querystrin <p>Try for example <a href="/?data=hello">/?data=hello</a></p> </body> </html>`) } res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @loige 8
  • 19. autocannon -c 200 --on-port / -- node server.js WRK node server.js& wrk -t8 -c200 -d10s http://localhost:8080/ @loige 9
  • 20. autocannon -c 200 --on-port /?data=ciao👋 -- node server.js Running 10s test @ http://localhost:8080/?data=ciao👋 200 connections ┌─────────┬─────────┬─────────┬─────────┬─────────┬────────────┬─────────┬────────────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼─────────┼─────────┼─────────┼─────────┼────────────┼─────────┼────────────┤ │ Latency │ 1899 ms │ 1951 ms │ 2053 ms │ 2054 ms │ 1964.92 ms │ 99.9 ms │ 3364.03 ms │ └─────────┴─────────┴─────────┴─────────┴─────────┴────────────┴─────────┴────────────┘ ┌───────────┬─────┬──────┬─────────┬────────┬────────┬────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤ │ Req/Sec │ 0 │ 0 │ 30 │ 199 │ 99.5 │ 94.27 │ 30 │ ├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤ │ Bytes/Sec │ 0 B │ 0 B │ 50.7 kB │ 336 kB │ 168 kB │ 159 kB │ 50.7 kB │ └───────────┴─────┴──────┴─────────┴────────┴────────┴────────┴─────────┘ Req/Bytes counts sampled once per second. 995 requests in 10.08s, 1.68 MB read @loige 10
  • 21. autocannon -c 200 --on-port /?data=ciao👋 -- node server.js Running 10s test @ http://localhost:8080/?data=ciao👋 200 connections ┌─────────┬─────────┬─────────┬─────────┬─────────┬────────────┬─────────┬────────────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼─────────┼─────────┼─────────┼─────────┼────────────┼─────────┼────────────┤ │ Latency │ 1899 ms │ 1951 ms │ 2053 ms │ 2054 ms │ 1964.92 ms │ 99.9 ms │ 3364.03 ms │ └─────────┴─────────┴─────────┴─────────┴─────────┴────────────┴─────────┴────────────┘ ┌───────────┬─────┬──────┬─────────┬────────┬────────┬────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤ │ Req/Sec │ 0 │ 0 │ 30 │ 199 │ 99.5 │ 94.27 │ 30 │ ├───────────┼─────┼──────┼─────────┼────────┼────────┼────────┼─────────┤ │ Bytes/Sec │ 0 B │ 0 B │ 50.7 kB │ 336 kB │ 168 kB │ 159 kB │ 50.7 kB │ └───────────┴─────┴──────┴─────────┴────────┴────────┴────────┴─────────┘ Req/Bytes counts sampled once per second. 995 requests in 10.08s, 1.68 MB read @loige 10
  • 22. ⛅ TIP 1-BIS ALSO, FIND OUT YOUR CEILING @loige 11
  • 23. import { createServer } from 'http' createServer((req, res) => { if (req.method === 'GET' && req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello Worldn') } else { res.statusCode = 404 res.end() } }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 @loige 12
  • 24. import { createServer } from 'http' createServer((req, res) => { if (req.method === 'GET' && req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello Worldn') } else { res.statusCode = 404 res.end() } }).listen(8080) 1 2 3 4 5 6 7 8 9 10 11 res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello Worldn') import { createServer } from 'http' 1 2 createServer((req, res) => { 3 if (req.method === 'GET' && req.url === '/') { 4 5 6 } else { 7 res.statusCode = 404 8 res.end() 9 } 10 }).listen(8080) 11 @loige 12
  • 25. autocannon -c 200 --on-port / -- node server.js Running 10s test @ http://localhost:8080/ 200 connections ┌─────────┬──────┬──────┬───────┬───────┬─────────┬─────────┬──────────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼──────┼──────┼───────┼───────┼─────────┼─────────┼──────────┤ │ Latency │ 3 ms │ 5 ms │ 11 ms │ 14 ms │ 5.51 ms │ 2.71 ms │ 80.63 ms │ └─────────┴──────┴──────┴───────┴───────┴─────────┴─────────┴──────────┘ ┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬─────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤ │ Req/Sec │ 21087 │ 21087 │ 34623 │ 35487 │ 33258.4 │ 4107.01 │ 21077 │ ├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤ │ Bytes/Sec │ 3.29 MB │ 3.29 MB │ 5.4 MB │ 5.54 MB │ 5.19 MB │ 641 kB │ 3.29 MB │ └───────────┴─────────┴─────────┴────────┴─────────┴─────────┴─────────┴─────────┘ Req/Bytes counts sampled once per second. 333k requests in 10.1s, 51.9 MB read @loige 13
  • 26. autocannon -c 200 --on-port / -- node server.js Running 10s test @ http://localhost:8080/ 200 connections ┌─────────┬──────┬──────┬───────┬───────┬─────────┬─────────┬──────────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼──────┼──────┼───────┼───────┼─────────┼─────────┼──────────┤ │ Latency │ 3 ms │ 5 ms │ 11 ms │ 14 ms │ 5.51 ms │ 2.71 ms │ 80.63 ms │ └─────────┴──────┴──────┴───────┴───────┴─────────┴─────────┴──────────┘ ┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬─────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤ │ Req/Sec │ 21087 │ 21087 │ 34623 │ 35487 │ 33258.4 │ 4107.01 │ 21077 │ ├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤ │ Bytes/Sec │ 3.29 MB │ 3.29 MB │ 5.4 MB │ 5.54 MB │ 5.19 MB │ 641 kB │ 3.29 MB │ └───────────┴─────────┴─────────┴────────┴─────────┴─────────┴─────────┴─────────┘ Req/Bytes counts sampled once per second. 333k requests in 10.1s, 51.9 MB read @loige 13
  • 27. 🍾 TIP 2. FIND YOUR BOTTLENECK @loige 14
  • 29. clinic doctor --autocannon [ -c 200 '/?data=ciao👋' ] -- node server.js @loige 16
  • 30. clinic flame --autocannon [ -c 200 '/?data=ciao👋' ] -- node server.js @loige 17
  • 31. clinic bubble --autocannon [ -c 200 '/?data=ciao👋' ] -- node server.js @loige 18
  • 32. 🎳 TIP 3. UNDERSTAND YOUR GOALS @loige 19
  • 33. WHAT DO WE OPTIMIZE FOR? THROUGHPUT? MEMORY? LATENCY? @loige 20
  • 35. I MEAN, IN PRODUCTION! Logs - Metrics - Traces @loige 22
  • 36. 🚀 TIP 5. SCALE YOUR ARCHITECTURE @loige 23
  • 38. HOW CAN WE SCALE A SYSTEM BY ADDING RESOURCES? @loige 25
  • 39. THE " " SCALE CUBE x-axis cloning z-axis partitioning y-axis functional decomposition @loige 26
  • 40. THE " " SCALE CUBE x-axis cloning z-axis partitioning y-axis functional decomposition @loige 26
  • 41. 27 Reverse proxy CLONING Inside the same server Load Balancer Using multiple server @loige
  • 49. import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 @loige 29
  • 50. import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 3-4x req/sec (8 core) @loige 29
  • 51. import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 import { cpus } from 'os' const numCPUs = cpus().length import cluster from 'cluster' 1 2 3 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 3-4x req/sec (8 core) @loige 29
  • 52. import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 import { cpus } from 'os' const numCPUs = cpus().length import cluster from 'cluster' 1 2 3 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 if (cluster.isMaster) { } else { } import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 11 // Worker code here... 12 13 3-4x req/sec (8 core) @loige 29
  • 53. import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 import { cpus } from 'os' const numCPUs = cpus().length import cluster from 'cluster' 1 2 3 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 if (cluster.isMaster) { } else { } import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 11 // Worker code here... 12 13 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 7 8 9 10 } else { 11 // Worker code here... 12 } 13 3-4x req/sec (8 core) @loige 29
  • 54. import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 import { cpus } from 'os' const numCPUs = cpus().length import cluster from 'cluster' 1 2 3 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 if (cluster.isMaster) { } else { } import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 11 // Worker code here... 12 13 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 7 8 9 10 } else { 11 // Worker code here... 12 } 13 // Worker code here... import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 12 } 13 3-4x req/sec (8 core) @loige 29
  • 55. import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 import { cpus } from 'os' const numCPUs = cpus().length import cluster from 'cluster' 1 2 3 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 // Worker code here... 12 } 13 if (cluster.isMaster) { } else { } import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 11 // Worker code here... 12 13 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 7 8 9 10 } else { 11 // Worker code here... 12 } 13 // Worker code here... import cluster from 'cluster' 1 import { cpus } from 'os' 2 3 const numCPUs = cpus().length 4 5 if (cluster.isMaster) { 6 // Fork workers 7 for (let i = 0; i < numCPUs; i++) { 8 cluster.fork() 9 } 10 } else { 11 12 } 13 import cluster from 'cluster' import { cpus } from 'os' const numCPUs = cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code here... } 1 2 3 4 5 6 7 8 9 10 11 12 13 3-4x req/sec (8 core) @loige 29
  • 56. YOU COULD ALSO USE CHECK OUT ! WORKER THREADS PISCINA @loige 30
  • 57. CLONING IS THE EASIEST STRATEGY TO SCALE A SERVICE...   ... AS LONG AS YOUR APPLICATION IS "STATELESS" @loige 31
  • 58. API Gateway FUNCTIONAL DECOMPOSITION a.k.a. "Micro-services" 32 /products /cart cart DB products DB @loige
  • 59. API Gateway FUNCTIONAL DECOMPOSITION a.k.a. "Micro-services" 33 /products /cart Functional decomposition can also be combined with cloning! cart DB products DB @loige
  • 60. NODE.JS IS GREAT FOR MICROSERVICES @loige 34
  • 61. MICROSERVICES CAN ALSO HELP WITH SCALING THE ORGANISATION! @loige 35
  • 63. PARTITIONING Service and Data Partitioning along Customer Boundaries Shard partitioning /products/[A-L]/ /products/[M-Z]/ DB 2 37 DB 1 @loige
  • 64. PARTITIONING IS GENERALLY USED TO SCALE DATABASES AND SAAS SOFTWARE GEOGRAPHICALLY @loige 38
  • 67. SUMMARY 🛫Establish a baseline 🍾Find your bottleneck @loige 39
  • 68. SUMMARY 🛫Establish a baseline 🍾Find your bottleneck 🎳Understand your goals @loige 39
  • 69. SUMMARY 🛫Establish a baseline 🍾Find your bottleneck 🎳Understand your goals 👁Always "observe" @loige 39
  • 70. SUMMARY 🛫Establish a baseline 🍾Find your bottleneck 🎳Understand your goals 👁Always "observe" 🚀Scale your architecture (cloning, decomposition & partitioning) @loige 39
  • 71. ☝nodejsdp.link THANK YOU! Special thanks to , , , , , , , , , , , , , , , , , , , , , Icons and SVGs by Photo by on @StefanoAbalsamo @matteocollina @dagonzago @NullishCoalesce @DublinSvelte @KViglucci @gjohnson391 @lucamaraschi @laurekamalandua @giltayar @mrm8488 @adrirai @harafise @EugeneWare @Jauny @tlivings @michaelcfine @leojino @shahidontech @Lordoomer @zsadigov @dottorblaster freepik.com Jonas Kaiser Unsplash @loige loige.link/node-adc 40