Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Node.js: scalability tips - Azure Dev Community Vijayawada

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

December 13, 2021
Tweet

More Decks by Luciano Mammino

Other Decks in Technology

Transcript

  1. NODE.JS SCALABILITY TIPS
    NODE.JS SCALABILITY TIPS
    Luciano Mammino ( )
    @loige
    loige.link/node-adc
    Vijayawada - 2021-12-13
    1

    View Slide

  2. @loige
    Get the slides!
    👇
    loige.link/node-adc
    2

    View Slide

  3. HELLO!
    3

    View Slide

  4. HELLO!
    👋 I'm Luciano (
    🍕🍝)
    3

    View Slide

  5. HELLO!
    👋 I'm Luciano (
    🍕🍝)
    Senior Architect @ fourTheorem (Dublin )
    3

    View Slide

  6. HELLO!
    👋 I'm Luciano (
    🍕🍝)
    Senior Architect @ fourTheorem (Dublin )
    nodejsdp.link
    📔 Co-Author of Node.js Design Patterns
    👉
    3

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  10. 🛫
    TIP 1.
    ESTABLISH A BASELINE
    @loige
    6

    View Slide

  11. /?data=ciao
    👋 @loige
    Site: - Code:
    qrgen.lmammino.repl.co replit.com/@lmammino/QRGen
    7

    View Slide

  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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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

    View Slide

  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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    14
    }
    15
    res.writeHead(200, { 'Content-Type': 'image/png' })
    16
    QRCode.toFileStream(res, data, { width: 300 })
    17
    }).listen(8080)
    18
    @loige
    8

    View Slide

  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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    14
    }
    15
    res.writeHead(200, { 'Content-Type': 'image/png' })
    16
    QRCode.toFileStream(res, data, { width: 300 })
    17
    18
    @loige
    8

    View Slide

  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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    14
    }
    15
    res.writeHead(200, { 'Content-Type': 'image/png' })
    16
    QRCode.toFileStream(res, data, { width: 300 })
    17
    }).listen(8080)
    18
    @loige
    8

    View Slide

  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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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

    View Slide

  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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    14
    }
    15
    16
    17
    }).listen(8080)
    18
    @loige
    8

    View Slide

  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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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(`
    9

    10
    To use this app you need to set the data querystrin
    11
    Try for example /?data=hello
    12

    13
    `)
    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(`

    To use this app you need to set the data querystrin
    Try for example /?data=hello

    `)
    }
    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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide


  22. TIP 1-BIS
    ALSO, FIND OUT YOUR CEILING
    @loige
    11

    View Slide

  23. 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

    View Slide

  24. 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

    View Slide

  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

    View Slide

  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

    View Slide

  27. 🍾
    TIP 2.
    FIND YOUR BOTTLENECK
    @loige
    14

    View Slide

  28. Clinic.js
    clinicjs.org
    @loige
    15

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. 🎳
    TIP 3.
    UNDERSTAND YOUR GOALS
    @loige
    19

    View Slide

  33. WHAT DO WE OPTIMIZE FOR?
    THROUGHPUT?
    MEMORY?
    LATENCY?
    @loige
    20

    View Slide

  34. 👁
    TIP 4.
    ALWAYS "OBSERVE"
    @loige
    21

    View Slide

  35. I MEAN, IN PRODUCTION!
    Logs - Metrics - Traces
    @loige
    22

    View Slide

  36. 🚀
    TIP 5.
    SCALE YOUR ARCHITECTURE
    @loige
    23

    View Slide

  37. PERFORMANCE != SCALABILITY
    @loige
    24

    View Slide

  38. HOW CAN WE SCALE A SYSTEM
    BY ADDING RESOURCES?
    @loige
    25

    View Slide

  39. THE " "
    SCALE CUBE
    x-axis
    cloning
    z-axis
    partitioning
    y-axis
    functional
    decomposition
    @loige
    26

    View Slide

  40. THE " "
    SCALE CUBE
    x-axis
    cloning
    z-axis
    partitioning
    y-axis
    functional
    decomposition
    @loige
    26

    View Slide

  41. 27
    Reverse proxy
    CLONING
    Inside the same server
    Load Balancer
    Using multiple server
    @loige

    View Slide

  42. THE MODULE
    CLUSTER
    Master process
    Worker
    process
    Worker
    process
    Worker
    process
    28
    @loige

    View Slide

  43. THE MODULE
    CLUSTER
    Master process
    Worker
    process
    Worker
    process
    Worker
    process
    28
    @loige

    View Slide

  44. THE MODULE
    CLUSTER
    Master process
    Worker
    process
    Worker
    process
    Worker
    process
    28
    @loige

    View Slide

  45. THE MODULE
    CLUSTER
    Master process
    Worker
    process
    Worker
    process
    Worker
    process
    28
    @loige

    View Slide

  46. THE MODULE
    CLUSTER
    Master process
    Worker
    process
    Worker
    process
    Worker
    process
    28
    @loige

    View Slide

  47. THE MODULE
    CLUSTER
    Master process
    Worker
    process
    Worker
    process
    Worker
    process
    28
    @loige

    View Slide

  48. THE MODULE
    CLUSTER
    Master process
    Worker
    process
    Worker
    process
    Worker
    process
    28
    @loige

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  56. YOU COULD ALSO USE
    CHECK OUT !
    WORKER
    THREADS
    PISCINA
    @loige
    30

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  60. NODE.JS IS GREAT FOR MICROSERVICES
    @loige
    34

    View Slide

  61. MICROSERVICES CAN ALSO HELP WITH
    SCALING THE ORGANISATION!
    @loige
    35

    View Slide

  62. MICROSERVICES ADD COMPLEXITY
    Observability
    Deployments
    Versioning
    Integration
    @loige
    36

    View Slide

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

    View Slide

  64. PARTITIONING IS GENERALLY USED
    TO SCALE DATABASES
    AND
    SAAS SOFTWARE GEOGRAPHICALLY
    @loige
    38

    View Slide

  65. SUMMARY @loige
    39

    View Slide

  66. SUMMARY
    🛫 Establish a baseline
    @loige
    39

    View Slide

  67. SUMMARY
    🛫 Establish a baseline
    🍾 Find your bottleneck
    @loige
    39

    View Slide

  68. SUMMARY
    🛫 Establish a baseline
    🍾 Find your bottleneck
    🎳 Understand your goals
    @loige
    39

    View Slide

  69. SUMMARY
    🛫 Establish a baseline
    🍾 Find your bottleneck
    🎳 Understand your goals
    👁 Always "observe"
    @loige
    39

    View Slide

  70. SUMMARY
    🛫 Establish a baseline
    🍾 Find your bottleneck
    🎳 Understand your goals
    👁 Always "observe"
    🚀 Scale your architecture
    (cloning, decomposition & partitioning)
    @loige
    39

    View Slide

  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

    View Slide