Share
Node.js: scalability tips - Azure Dev Community Vijayawada
You finally built that amazing start-up idea you had in mind for years and you did it using Node.js! That's Great! You just launched it on Hacker News and you are very happy and proud... but now more and more people are using it and you start to have a nasty fear that Node.js won't scale because you now... it's single-threaded! Is your project doomed now? Do you have to invest your time on rewriting it in something like C++ or maybe Rust or even Go? You'd rather invest your time on adding valuable features for your users rather than learning a new language and rewriting everything from scratch, but what if nothing works anymore? And... by the way, what the heck "single-threaded" really means?! Fear no more, dear fellow developer! In this talk, we will discuss the architecture of Node.js going through its strengths and its weaknesses. We will then talk about scalability and I will share some valuable tips and tricks to make your Node.js app scale! Spoiler alert: you probably won't need Go or Rust :)
Luciano Mammino
Transcript
71 slides
-
NODE.JS SCALABILITY TIPS NODE.JS SCALABILITY TIPS Luciano Mammino ( )
@loige loige.link/node-adc Vijayawada - 2021-12-13 1 -
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 -
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 -
import { createServer } from '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 @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 World\n') } 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 World\n') 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 -
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 -
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 -
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 -
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 🛫 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