Episode ini membahas membuat aplikasi WebSocket lebih cepat: optimasi koneksi dan payload, clustering Node.js dengan worker, pemantauan event loop, debounce di klien, serta optimasi jaringan dan TLS.

Aplikasi sudah berfungsi, tapi apakah ia cukup cepat? Di episode ini kita mengukur dan memperbaiki: menekan latensi, mengurangi bandwidth, memanfaatkan semua inti CPU, dan memastikan event loop Node.js tidak tersendat.
Episode 17 membahas performance optimization dari empat sisi: koneksi, pesan, server, dan klien. Kalian akan belajar teknik yang langsung bisa diterapkan, mulai dari kompresi sampai clustering.
Koneksi WebSocket yang sehat perlu disetel agar tidak boros dan tidak mati sia-sia.
sysctl net.ipv4.tcp_keepalive_time
sysctl net.ipv4.tcp_keepalive_intvl
sysctl net.ipv4.tcp_keepalive_probesParameter tcp_keepalive_time menentukan kapan kernel mulai mengirim paket keep-alive. Untuk koneksi panjang seperti WebSocket, nilai yang wajar (misalnya 60 detik) membuat NAT dan load balancer tidak menghapus koneksi idle.
Buffer TCP yang terlalu kecil menahan throughput, terlalu besar menahan memori.
const wss = new WebSocketServer({
port: 8080,
perMessageDeflate: true,
maxPayload: 64 * 1024,
});maxPayload: 64 * 1024 membatasi ukuran pesan sehingga satu pesan raksasa tidak menghabiskan memori. Batasan ini juga mencegah serangan yang mengirim payload sangat besar.
Ukuran pesan adalah bandwidth dan latensi. Tiga cara menguranginya:
const pesan = {
t: "chat", // singkatan tipe
r: "node", // singkatan room
m: "Halo!", // isi pesan
};
ws.send(JSON.stringify(pesan));type menjadi t, message menjadi m. Hemat 30-50 persen.Mengirim seratus pesan kecil lebih mahal daripada satu pesan besar.
const antrean = [];
setInterval(() => {
if (antrean.length === 0) return;
ws.send(JSON.stringify({
type: "batch",
items: antrean.splice(0, antrean.length),
}));
}, 100);antrean.splice(0, antrean.length) mengambil semua update yang terkumpul dalam 100 ms. Posisi mouse yang diupdate 60 kali per detik cukup dikirim 10 kali per detik dalam satu batch.
Node.js berjalan pada satu inti CPU. Cluster memanfaatkan semua inti.
const cluster = require("cluster");
const os = require("os");
if (cluster.isPrimary) {
const jumlah = os.cpus().length;
for (let i = 0; i < jumlah; i++) {
cluster.fork();
}
cluster.on("exit", (worker) => {
console.log("worker mati, fork ulang:", worker.process.pid);
cluster.fork();
});
} else {
const { WebSocketServer } = require("ws");
const wss = new WebSocketServer({ port: 8080 });
wss.on("connection", (ws) => ws.send("terhubung ke worker " + process.pid));
}cluster.isPrimary membedakan proses utama dan worker. Setiap worker melayani koneksi sendiri di port yang sama. Proses utama mem-fork ulang worker yang mati untuk menjaga ketersediaan.
Event loop yang tersumbat memperlambat semua koneksi. Ukur delay-nya secara rutin.
const mulai = Date.now();
setInterval(() => {
const delay = Date.now() - mulai;
if (delay > 100) {
console.warn("event loop tersumbat:", delay, "ms");
}
mulai = Date.now();
}, 100);Date.now() - mulai mengukur selisih antara interval yang dijadwalkan dan yang benar-benar berjalan. Delay di atas 100 ms menandakan tugas sinkron yang terlalu berat — biasanya JSON.parse raksasa atau operasi blocking — harus dipindah ke worker thread.
Klien yang hemat membuat server lebih ringan dan pengalaman lebih mulus.
let lastSend = 0;
canvas.onmousemove = (e) => {
const kini = Date.now();
if (kini - lastSend < 50) return;
lastSend = kini;
ws.send(JSON.stringify({ type: "pointer", x: e.x, y: e.y }));
};kini - lastSend < 50 membatasi pengiriman maksimal 20 kali per detik. Untuk daftar pesan panjang, virtual scrolling merender hanya baris yang terlihat, sehingga browser tidak membeku saat ribuan pesan masuk.
Jaringan yang dioptimalkan menambah latensi dari sisi infrastruktur.
Perlu diingat: load balancer menambah satu hop. Ukur latensi end-to-end sebelum dan sesudah menambah lapisan infrastruktur.
Episode 17 memberi kalian kacamata performa: setiap byte pesan, setiap tugas sinkron, dan setiap lapisan jaringan berpotensi menjadi bottleneck. Optimasi dimulai dari pengukuran, bukan dari tebakan.
Inti yang harus dibawa pulang:
Di episode 18 berikutnya kita membahas monitoring & observability: metrik koneksi dan pesan, logging, integrasi Prometheus dan Grafana, distributed tracing dengan OpenTelemetry, serta alerting.