Slide 1

Slide 1 text

NODE.JS SCALABILITY TIPS Luciano Mammino ( ) @loige loige.link/nodescale 1

Slide 2

Slide 2 text

HELLO, I AM LUCIANO! Cloud Architect and Fullstack developer Blog: Twitter: GitHub: loige.co @loige @lmammino nodejsdp.link 2

Slide 3

Slide 3 text

@loige Get the slides! loige.link/nodescale 3

Slide 4

Slide 4 text

"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 4

Slide 5

Slide 5 text

TIP 1. ESTABLISH A BASELINE @loige 5

Slide 6

Slide 6 text

/?data=ciao @loige https://repl.it/@lmammino/QRGen 6

Slide 7

Slide 7 text

const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 @loige 7

Slide 8

Slide 8 text

const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 @loige 7

Slide 9

Slide 9 text

const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 @loige 7

Slide 10

Slide 10 text

const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 @loige 7

Slide 11

Slide 11 text

const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 if (!data) { res.writeHead(400) // bad request return res.end() } const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 8 9 10 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 @loige 7

Slide 12

Slide 12 text

const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 if (!data) { res.writeHead(400) // bad request return res.end() } const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 8 9 10 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 12 13 }) 14 .listen(8080) 15 @loige 7

Slide 13

Slide 13 text

const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 if (!data) { res.writeHead(400) // bad request return res.end() } const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 8 9 10 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 12 13 }) 14 .listen(8080) 15 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('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(400) // bad request return res.end() } 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 @loige 7

Slide 14

Slide 14 text

autocannon -c 200 --on-port / -- node server.js WRK node server.js& wrk -t8 -c200 -d10s http://localhost:8080/ @loige 8

Slide 15

Slide 15 text

autocannon -c 200 --on-port /?data=hello%20cityjs -- 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 9

Slide 16

Slide 16 text

autocannon -c 200 --on-port /?data=hello%20cityjs -- 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 9

Slide 17

Slide 17 text

⛅ TIP 1-BIS ALSO, FIND OUT YOUR CEILING @loige 10

Slide 18

Slide 18 text

const { createServer } = require('http') createServer((req, res) => { if (req.method === 'GET' && req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello World\n') } else { res.statusCode = 404 res.end() } }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 @loige 11

Slide 19

Slide 19 text

const { createServer } = require('http') createServer((req, res) => { if (req.method === 'GET' && req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello World\n') } else { res.statusCode = 404 res.end() } }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello World\n') const { createServer } = require('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 }) 11 .listen(8080) 12 @loige 11

Slide 20

Slide 20 text

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 12

Slide 21

Slide 21 text

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 12

Slide 22

Slide 22 text

TIP 2. FIND YOUR BOTTLENECK @loige 13

Slide 23

Slide 23 text

Clinic.js clinicjs.org @loige 14

Slide 24

Slide 24 text

clinic doctor --autocannon [ -c 200 '/?data=ciao ' ] -- node server.js @loige 15

Slide 25

Slide 25 text

clinic flame --autocannon [ -c 200 '/?data=ciao ' ] -- node server.js @loige 16

Slide 26

Slide 26 text

clinic bubble --autocannon [ -c 200 '/?data=ciao ' ] -- node server.js @loige 17

Slide 27

Slide 27 text

TIP 3. UNDERSTAND YOUR GOALS @loige 18

Slide 28

Slide 28 text

WHAT DO WE OPTIMIZE FOR? THROUGHPUT? MEMORY? LATENCY? @loige 19

Slide 29

Slide 29 text

TIP 4. ALWAYS "OBSERVE" @loige 20

Slide 30

Slide 30 text

I MEAN, IN PRODUCTION! Logs - Metrics - Traces @loige 21

Slide 31

Slide 31 text

TIP 5. SCALE YOUR ARCHITECTURE @loige 22

Slide 32

Slide 32 text

PERFORMANCE != SCALABILITY @loige 23

Slide 33

Slide 33 text

HOW CAN WE SCALE A SYSTEM BY ADDING RESOURCES? @loige 24

Slide 34

Slide 34 text

THE " " SCALE CUBE x-axis cloning z-axis partitioning y-axis functional decomposition @loige 25

Slide 35

Slide 35 text

THE " " SCALE CUBE x-axis cloning z-axis partitioning y-axis functional decomposition @loige 25

Slide 36

Slide 36 text

26 Reverse proxy CLONING Inside the same server Load Balancer Using multiple server @loige

Slide 37

Slide 37 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 27 @loige

Slide 38

Slide 38 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 27 @loige

Slide 39

Slide 39 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 27 @loige

Slide 40

Slide 40 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 27 @loige

Slide 41

Slide 41 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 27 @loige

Slide 42

Slide 42 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 27 @loige

Slide 43

Slide 43 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 27 @loige

Slide 44

Slide 44 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 @loige 28

Slide 45

Slide 45 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 @loige 28

Slide 46

Slide 46 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 @loige 28

Slide 47

Slide 47 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 @loige 28

Slide 48

Slide 48 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 5 6 7 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 @loige 28

Slide 49

Slide 49 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 5 6 7 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 // Worker code require('./server.js') const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 10 11 } 12 @loige 28

Slide 50

Slide 50 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 5 6 7 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 // Worker code require('./server.js') const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 10 11 } 12 const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 @loige 28

Slide 51

Slide 51 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 5 6 7 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 // Worker code require('./server.js') const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 10 11 } 12 const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 3-4x req/sec (8 core) @loige 28

Slide 52

Slide 52 text

YOU COULD ALSO USE CHECK OUT ! WORKER THREADS PISCINA @loige 29

Slide 53

Slide 53 text

CLONING IS THE EASIEST STRATEGY TO SCALE A SERVICE... ... AS LONG AS YOUR APPLICATION IS "STATELESS" @loige 30

Slide 54

Slide 54 text

API Gateway FUNCTIONAL DECOMPOSITION a.k.a. "Micro-services" 31 /products /cart cart DB products DB @loige

Slide 55

Slide 55 text

API Gateway FUNCTIONAL DECOMPOSITION a.k.a. "Micro-services" 32 /products /cart Functional decomposition can also be combined with cloning! cart DB products DB @loige

Slide 56

Slide 56 text

NODE.JS IS GREAT FOR MICROSERVICES @loige 33

Slide 57

Slide 57 text

MICROSERVICES CAN ALSO HELP WITH SCALING THE ORGANISATION! @loige 34

Slide 58

Slide 58 text

MICROSERVICES ADD COMPLEXITY Observability Deployments Versioning Integration @loige 35

Slide 59

Slide 59 text

PARTITIONING Service and Data Partitioning along Customer Boundaries Shard partitioning /products/[A-L]/ /products/[M-Z]/ DB 2 36 DB 1 @loige

Slide 60

Slide 60 text

PARTITIONING IS GENERALLY USED TO SCALE DATABASES AND SAAS SOFTWARE GEOGRAPHICALLY @loige 37

Slide 61

Slide 61 text

SUMMARY @loige 38

Slide 62

Slide 62 text

SUMMARY Establish a baseline @loige 38

Slide 63

Slide 63 text

SUMMARY Establish a baseline Find your bottleneck @loige 38

Slide 64

Slide 64 text

SUMMARY Establish a baseline Find your bottleneck Understand your goals @loige 38

Slide 65

Slide 65 text

SUMMARY Establish a baseline Find your bottleneck Understand your goals Always "observe" @loige 38

Slide 66

Slide 66 text

SUMMARY Establish a baseline Find your bottleneck Understand your goals Always "observe" Scale your architecture (cloning, decomposition & partitioning) @loige 38

Slide 67

Slide 67 text

THANK YOU! Special thanks to , , , , , , , , , , , , , , , , , , , , , Icons and SVGs by Cover picture 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 Markus Spiske Unsplash @loige loige.link/nodescale 39