Belajar ExpressJS - Scaling & High Availability
Episode 25 of 28

Belajar ExpressJS - Scaling & High Availability

Menskala aplikasi Express secara horizontal: load balancer, stateless session dan session store bersama, Socket.IO Redis adapter, observability dengan metrik, serta load testing dan tuning.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Episode 24 membuat aplikasi berjalan di satu instance. Episode 25 menjawab pertanyaan berikutnya: apa yang terjadi saat satu instance tidak cukup? Scaling horizontal — menambah instance, bukan memperbesar satu mesin — adalah jawaban standar, dan ia datang dengan tantangan unik.

Mengapa scaling sulit untuk aplikasi Express? Karena aplikasi dengan state di memori lokal akan patah saat disebar ke banyak instance: sesi yang dibuat instance A tidak dikenal instance B, dan event Socket.IO instance A tidak sampai ke client instance B. Episode ini menyelesaikan semuanya — membuat aplikasi stateless sehingga banyak instance bekerja sebagai satu kesatuan.

Scaling Horizontal dan Load Balancer

Model Arsitektur

100%

Semua instance berbagi state eksternal (database dan Redis) — tidak ada yang disimpan di memori instance. Inilah definisi aplikasi stateless.

nginx sebagai Load Balancer

Load balancing upstream
upstream express_api {
    least_conn;
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
    server 10.0.0.13:3000;
}
 
server {
    listen 443 ssl;
    server_name api.example.com;
 
    location / {
        proxy_pass http://express_api;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Request-Id $request_id;
    }
}
  • least_conn mengirim ke instance dengan koneksi paling sedikit.
  • Header X-Forwarded-* dibutuhkan aplikasi untuk tahu IP asli client — dan wajib mengaktifkan trust proxy:
JSTrust proxy untuk akses asal client
app.set("trust proxy", 1)

Tanpa trust proxy, req.ip menunjuk ke IP load balancer dan rate limit (episode 20) menganggap semua user satu IP. Ini alasan mengapa trust proxy disebut berkali-kali di series ini.

Important

trust proxy adalah keputusan keamanan: hanya aktifkan jika kalian yakin permintaan berasal dari proxy tepercaya. Mengaktifkannya tanpa proxy justru membiarkan client mengirim header X-Forwarded-For palsu dan memalsukan IP.

Stateless Session

Masalah Session di Memori

express-session default memakai MemoryStore (episode 12) — data sesi hidup di memori instance. Di instance A user login; di instance B (tempat request berikutnya dikirim) sesinya "tidak ada".

Store Bersama: connect-redis

Solusinya: pindahkan session ke Redis yang dibagikan semua instance:

Install connect-redis
npm install connect-redis
JSSession store Redis
import session from "express-session"
import RedisStore from "connect-redis"
import { Redis } from "ioredis"
import { config } from "../config.js"
 
const redisClient = new Redis(config.redisUrl)
 
app.use(
  session({
    store: new RedisStore({ client: redisClient }),
    secret: config.sessionSecret,
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      sameSite: "lax",
      secure: config.isProduction,
    },
  })
)

Sekarang sesi "tinggal" di Redis — instance mana pun yang melayani request membaca sesi yang sama. Manfaat tambahan: sesi bertahan saat instance di-restart (dibanding MemoryStore yang hilang).

Stateless JWT (Episode 12)

Jika memakai JWT, kalian sudah stateless — token membawa semuanya. Ini alasan JWT begitu populer untuk API diskala. Konsekuensinya: invalidasi token (logout server-side, pemblokiran akun) harus dikelola eksplisit (misal denylist di Redis) karena server tidak menyimpan state sesi.

PendekatanStateScalingLogout/block server-side
Session cookie + Redis storeRedisPerlu store bersamaMudah (hapus dari Redis)
JWT statelessTokenTanpa storePerlu denylist eksplisit

Socket.IO Redis Adapter

Episode 14 berjanji: event Socket.IO lintas instance butuh Redis adapter. Tepatnya sekarang:

Install Redis adapter
npm install @socket.io/redis-adapter
JSSocket.IO Redis adapter
import { Server } from "socket.io"
import { createAdapter } from "@socket.io/redis-adapter"
import { Redis } from "ioredis"
import { config } from "../config.js"
 
const pubClient = new Redis(config.redisUrl)
const subClient = pubClient.duplicate()
 
const io = new Server(httpServer, {
  cors: { origin: config.corsOrigins },
  adapter: createAdapter(pubClient, subClient),
})

Adapter memakai dua koneksi Redis (pub/sub): saat instance A melakukan io.emit, event disebarkan lewat Redis sehingga client yang terhubung ke instance B juga menerimanya. Tanpa ini, realtime aplikasi kalian hanya menjangkau sebagian pengguna.

Observability dan Health

Metrik Prometheus

Install prom-client
npm install prom-client
JSMetrik dasar Prometheus
import client from "prom-client"
 
client.collectDefaultMetrics()
 
const httpRequests = new client.Counter({
  name: "http_requests_total",
  help: "Total HTTP request",
  labelNames: ["method", "status"],
})
 
app.use((req, res, next) => {
  res.on("finish", () => {
    httpRequests.inc({ method: req.method, status: res.statusCode })
  })
  next()
})
 
app.get("/metrics", async (req, res) => {
  res.set("Content-Type", client.register.contentType)
  res.send(await client.register.metrics())
})

Endpoint /metrics dibaca Prometheus, divisualisasikan di Grafana — bersama dengan log structured episode 17. Tiga sinyal observability (log + metrik + health) membentuk dasar operasi aplikasi diskala.

Health Check untuk Orchestrator

Orchestrator memakai health check (episode 24) untuk mengganti instance yang tidak sehat — prasyarat high availability: instance mati tidak berarti aplikasi mati, selama ada instance lain yang melayani.

Load Testing dan Tuning

autocannon

Uji kapasitas sebelum dan sesudah tuning:

Install autocannon
npm install -g autocannon
Load test dasar
autocannon -c 100 -d 30 http://localhost:3000/users
  • -c 100 — 100 koneksi konkuren.
  • -d 30 — selama 30 detik.

Output menampilkan throughput (req/s), latency percentiles, dan jumlah error — ukuran objektif kapasitas aplikasi.

Variabel Tuning

Setelah mengukur, tiga area yang paling sering di-tuning:

AreaParameterPengaruh
ConcurrencyJumlah instance (-i max PM2)Throughput naik sebanding CPU
DatabaseConnection pool (max)Hindari pool exhaustion
RedisSession/cache/rate-limit sharedState terdistribusi

Mengukur Perubahan

Aturan mutlak tuning: satu perubahan satu pengukuran. Ganti max pool, uji lagi dengan parameter sama, bandingkan. Tanpa pengukuran berulang, tuning hanyalah perasaan.

Tip

Mulai load test dari nilai kecil (misal 20 koneksi) lalu naik bertahap sambil memperhatikan latency percentile p99. Fokus tuning adalah latency stabil di bawah target, bukan sekadar throughput maksimum — aplikasi yang sukses menekan p99 akan terasa cepat untuk semua pengguna.

Common Pitfalls

Scale Tanpa Redis Store

Rate limit (episode 20), session (episode 12), dan Socket.IO (episode 14) semuanya butuh store bersama. Diskala tanpa Redis = proteksi dan state melenceng antar instance.

MemoryStore di Produksi

MemoryStore bukan hanya non-scalable — ia juga hilang saat restart. Tidak ada alasan memakainya di produksi.

Trust Proxy Dimatikan

Tanpa trust proxy, req.ip salah dan rate limit hancur saat di belakang LB. Hidupkan — dengan memahami risiko header palsu bila tidak ada proxy.

Health Check Terlalu Dangkal

Health check yang hanya res.send("ok") tidak mendeteksi database mati. Periksa ketergantungan nyata seperti episode 24.

Penutup

Episode 25 membuat aplikasi siap diskala: load balancer dengan trust proxy, session dan rate limit berbasis Redis store, Socket.IO Redis adapter, observability dengan Prometheus, serta load testing dengan autocannon dan tuning yang terukur.

Inti yang harus dibawa pulang:

  • Stateless adalah prasyarat scaling: semua state pindah ke Redis/database.
  • trust proxy wajib diaktifkan saat di belakang load balancer.
  • Session cookie + Redis store, atau JWT stateless — dua jalur sah, pilih sesuai kebutuhan.
  • Socket.IO butuh Redis adapter agar event lintas instance.
  • Observability = log (episode 17) + metrik + health check.
  • Tuning berbasis pengukuran (autocannon): satu perubahan, satu pengukuran.

Di episode 26 selanjutnya kita akan melihat gambaran besar: ekosistem & tren modern 2026 — Express 5 vs 4, migrasi, dan perbandingan dengan Fastify, NestJS, dan Hono. Sampai jumpa di episode 26!

Belajar ExpressJS - Scaling & High Availability | Belajar ExpressJS