Web Development

WebSocket: Real-time Communication di Web

Tutorial lengkap WebSocket — protokol, implementasi di Node.js dan browser, rooms, scaling, heartbeat, dan real-time application patterns

1. Pengenalan WebSocket

WebSocket adalah protokol komunikasi yang menyediakan koneksi dua arah (full-duplex) antara client (browser) dan server melalui satu koneksi TCP yang persisten. Berbeda dari HTTP yang bersifat request-response, WebSocket memungkinkan server mengirim data ke client kapan saja tanpa diminta.

Mengapa WebSocket?

HTTP tradisional bekerja dengan model request-response: client mengirim request, server mengirim response, lalu koneksi ditutup. Untuk aplikasi real-time seperti chat, game, atau live dashboard, model ini sangat tidak efisien karena:

Teknik Cara Kerja Kelebihan Kekurangan
Polling Client request berulang Sederhana Boros bandwidth, latency tinggi
Long Polling Request ditahan sampai ada data Lebih efisien Overhead HTTP headers tiap request
Server-Sent Events Server push via HTTP Satu arah, mudah Hanya server → client
WebSocket Koneksi persisten dua arah Low latency, real-time, efisien Butuh setup lebih, scaling lebih kompleks

Use Cases

💡 Tips

WebSocket bukan selalu jawaban terbaik. Jika kamu hanya butuh server → client (satu arah), Server-Sent Events (SSE) lebih sederhana. Jika datanya jarang berubah, polling mungkin cukup.

2. WebSocket Protocol

WebSocket menggunakan protokol ws:// (atau wss:// untuk SSL/TLS) dan dimulai dengan HTTP Upgrade handshake. Setelah handshake berhasil, koneksi beralih dari HTTP ke WebSocket dan bisa mengirim data secara dua arah.

Handshake Process

HTTP
# Client Request (HTTP Upgrade)
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

# Server Response (101 Switching Protocols)
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Frame Format

Setelah koneksi terbuka, data dikirim dalam bentuk WebSocket frames. Setiap frame memiliki header kecil yang berisi informasi seperti tipe data (text, binary, close, ping, pong) dan masking:

Text
Frame Structure:
┌─────────┬─────────┬────────────┬────────────────┐
│ FIN(1)  │ RSV(3)  │ Opcode(4)  │ MASK + LEN     │
├─────────┼─────────┼────────────┼────────────────┤
│ 1 bit   │ 3 bit   │ 0x1 = Text │ 1 bit mask     │
│         │         │ 0x2 = Bin  │ 7 bit length   │
│         │         │ 0x8 = Close│ + extended len  │
│         │         │ 0x9 = Ping │                │
│         │         │ 0xA = Pong │                │
├─────────┴─────────┴────────────┴────────────────┤
│ Masking Key (32 bit, jika mask=1)               │
├─────────────────────────────────────────────────┤
│ Payload Data (Application Data)                 │
└─────────────────────────────────────────────────┘

WebSocket vs HTTP

Aspek HTTP WebSocket
Koneksi Baru setiap request Persisten (satu koneksi)
Arah Client → Server Dua arah (full-duplex)
Overhead Headers di setiap request Headers hanya saat handshake
Latency Tinggi Sangat rendah
Data Format Teks/binary (HTTP message) Teks/binary (frames)
Scalability Mudah (stateless) Lebih kompleks (stateful)

3. WebSocket Native API (Browser)

Browser modern sudah memiliki WebSocket API bawaan. Ini adalah API yang paling dasar — tanpa library tambahan. Mari kita pelajari cara menggunakannya.

Membuat Koneksi

JavaScript
// Membuat koneksi WebSocket baru
const ws = new WebSocket('ws://localhost:8080');

// Atau dengan secure connection (wss://)
const wsSecure = new WebSocket('wss://api.example.com/ws');

// Dengan protocols (opsional)
const wsWithProtocol = new WebSocket('ws://localhost:8080', ['chat', 'notifications']);

Event Handlers

JavaScript
const ws = new WebSocket('ws://localhost:8080');

// Event: koneksi berhasil dibuka
ws.onopen = (event) => {
  console.log('✅ Terhubung ke WebSocket server');
  console.log('URL:', ws.url);
  console.log('Protocol:', ws.protocol);

  // Kirim pesan pertama setelah terhubung
  ws.send(JSON.stringify({
    type: 'join',
    username: 'Budi',
    room: 'general',
  }));
};

// Event: menerima pesan dari server
ws.onmessage = (event) => {
  console.log('📩 Pesan diterima:', event.data);

  // Parse jika data berupa JSON
  try {
    const data = JSON.parse(event.data);
    switch (data.type) {
      case 'chat':
        displayMessage(data.username, data.message);
        break;
      case 'user_joined':
        showNotification(`${data.username} bergabung`);
        break;
      case 'user_left':
        showNotification(`${data.username} keluar`);
        break;
    }
  } catch (e) {
    console.log('Raw message:', event.data);
  }
};

// Event: koneksi ditutup
ws.onclose = (event) => {
  console.log('❌ Koneksi ditutup');
  console.log('Code:', event.code);
  console.log('Reason:', event.reason);
  console.log('Clean close:', event.wasClean);

  // Reconnect jika bukan close yang disengaja
  if (!event.wasClean) {
    setTimeout(() => reconnect(), 3000);
  }
};

// Event: error
ws.onerror = (error) => {
  console.error('🔴 WebSocket error:', error);
};

Mengirim Data

JavaScript
// Kirim teks
ws.send('Hello, Server!');

// Kirim JSON
ws.send(JSON.stringify({
  type: 'chat',
  message: 'Halo semua!',
  timestamp: Date.now(),
}));

// Kirim binary data (Blob)
const blob = new Blob(['binary data'], { type: 'application/octet-stream' });
ws.send(blob);

// Kirim binary data (ArrayBuffer)
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setInt32(0, 42);
ws.send(buffer);

// Cek status koneksi sebelum mengirim
function safeSend(data) {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(typeof data === 'string' ? data : JSON.stringify(data));
    return true;
  }
  console.warn('WebSocket tidak terbuka, pesan tidak terkirim');
  return false;
}

WebSocket States

State Constant Penjelasan
CONNECTING WebSocket.CONNECTING (0) Koneksi sedang dibuat
OPEN WebSocket.OPEN (1) Koneksi terbuka, bisa mengirim/menerima
CLOSING WebSocket.CLOSING (2) Koneksi sedang ditutup
CLOSED WebSocket.CLOSED (3) Koneksi sudah ditutup

4. WebSocket di Node.js

Untuk membuat WebSocket server di Node.js, kita bisa menggunakan library ws yang sangat ringan dan populer.

Instalasi

Bash
# Instal ws library
npm install ws

# Instal types untuk TypeScript
npm install -D @types/ws

Basic WebSocket Server

TypeScript
import { WebSocketServer, WebSocket } from 'ws';

// Buat WebSocket server
const wss = new WebSocketServer({ port: 8080 });

// Set koneksi baru
wss.on('connection', (ws: WebSocket, req) => {
  const clientIp = req.socket.remoteAddress;
  console.log(`Client terhubung dari ${clientIp}`);
  console.log(`Total klien: ${wss.clients.size}`);

  // Kirim welcome message
  ws.send(JSON.stringify({
    type: 'welcome',
    message: 'Selamat datang di WebSocket server!',
    totalClients: wss.clients.size,
  }));

  // Handle pesan masuk
  ws.on('message', (data) => {
    const message = data.toString();
    console.log('Pesan diterima:', message);

    try {
      const parsed = JSON.parse(message);

      switch (parsed.type) {
        case 'chat':
          // Broadcast ke semua client lain
          wss.clients.forEach((client) => {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
              client.send(JSON.stringify({
                type: 'chat',
                username: parsed.username,
                message: parsed.message,
                timestamp: Date.now(),
              }));
            }
          });
          break;

        case 'ping':
          ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
          break;
      }
    } catch (e) {
      // Echo pesan kembali
      ws.send(`Echo: ${message}`);
    }
  });

  // Handle koneksi ditutup
  ws.on('close', (code, reason) => {
    console.log(`Client terputus: ${code} - ${reason}`);
  });

  // Handle error
  ws.on('error', (error) => {
    console.error('WebSocket error:', error.message);
  });
});

console.log('WebSocket server berjalan di ws://localhost:8080');

WebSocket Server dengan Express + HTTP

TypeScript
import express from 'express';
import { createServer } from 'http';
import { WebSocketServer } from 'ws';

const app = express();
const server = createServer(app);

// Attach WebSocket ke HTTP server yang sama
const wss = new WebSocketServer({ server });

// REST API endpoint (biasa)
app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    wsClients: wss.clients.size,
    uptime: process.uptime(),
  });
});

// WebSocket handler
wss.on('connection', (ws) => {
  console.log('New WebSocket connection');

  ws.on('message', (data) => {
    // Broadcast ke semua client
    wss.clients.forEach((client) => {
      if (client.readyState === ws.OPEN) {
        client.send(data.toString());
      }
    });
  });
});

// Start server di port yang sama
server.listen(3000, () => {
  console.log('Server berjalan di http://localhost:3000');
  console.log('WebSocket tersedia di ws://localhost:3000');
});

5. Socket.IO Library

Socket.IO adalah library paling populer untuk real-time communication. Socket.IO dibangun di atas WebSocket tetapi menambahkan banyak fitur penting: auto-reconnection, rooms, namespaces, binary support, dan fallback ke polling jika WebSocket tidak tersedia.

Instalasi

Bash
# Server
npm install socket.io

# Client (untuk Node.js client)
npm install socket.io-client

Socket.IO Server

TypeScript
import express from 'express';
import { createServer } from 'http';
import { Server } from 'socket.io';

const app = express();
const server = createServer(app);
const io = new Server(server, {
  cors: {
    origin: 'http://localhost:5173',
    methods: ['GET', 'POST'],
  },
  pingTimeout: 60000,
  pingInterval: 25000,
});

// Middleware: autentikasi
io.use((socket, next) => {
  const token = socket.handshake.auth.token;
  if (isValidToken(token)) {
    socket.data.user = decodeToken(token);
    next();
  } else {
    next(new Error('Authentication error'));
  }
});

// Handle connection
io.on('connection', (socket) => {
  console.log(`User terhubung: ${socket.id}`);
  console.log(`Username: ${socket.data.user?.name}`);

  // Custom events
  socket.on('chat:message', (data) => {
    console.log(`Pesan dari ${data.username}: ${data.message}`);

    // Broadcast ke semua client kecuali pengirim
    socket.broadcast.emit('chat:message', {
      username: data.username,
      message: data.message,
      timestamp: Date.now(),
    });
  });

  socket.on('chat:typing', (data) => {
    socket.broadcast.emit('chat:typing', {
      username: data.username,
      isTyping: data.isTyping,
    });
  });

  socket.on('disconnect', (reason) => {
    console.log(`User ${socket.id} terputus: ${reason}`);
  });
});

server.listen(3000, () => {
  console.log('Socket.IO server running on port 3000');
});

Socket.IO Client (Browser)

HTML/JavaScript
<!-- Include Socket.IO client dari CDN -->
<script src="https://cdn.socket.io/4.7.4/socket.io.min.js"></script>

<script>
  // Koneksi ke server
  const socket = io('http://localhost:3000', {
    auth: { token: 'jwt-token-kamu' },
    reconnection: true,
    reconnectionAttempts: 5,
    reconnectionDelay: 1000,
  });

  // Event: terhubung
  socket.on('connect', () => {
    console.log('✅ Terhubung:', socket.id);
  });

  // Event: menerima pesan
  socket.on('chat:message', (data) => {
    console.log(`${data.username}: ${data.message}`);
    addMessageToUI(data);
  });

  // Event: user sedang mengetik
  socket.on('chat:typing', (data) => {
    if (data.isTyping) {
      showTypingIndicator(data.username);
    } else {
      hideTypingIndicator(data.username);
    }
  });

  // Event: disconnect
  socket.on('disconnect', (reason) => {
    console.log('❌ Terputus:', reason);
  });

  // Event: error
  socket.on('connect_error', (error) => {
    console.error('Koneksi gagal:', error.message);
  });

  // Kirim pesan chat
  function sendMessage(message) {
    socket.emit('chat:message', {
      username: currentUser.name,
      message: message,
    });
  }

  // Kirim status typing
  function setTyping(isTyping) {
    socket.emit('chat:typing', {
      username: currentUser.name,
      isTyping: isTyping,
    });
  }
</script>

6. Rooms & Namespaces

Socket.IO menyediakan konsep Rooms dan Namespaces untuk mengorganisir koneksi. Rooms memungkinkan kamu mengirim pesan ke subset client tertentu, sementara Namespaces membagi logika ke beberapa endpoint.

Rooms

TypeScript
io.on('connection', (socket) => {

  // Bergabung ke room
  socket.on('join:room', (roomId) => {
    socket.join(roomId);
    console.log(`${socket.id} joined room ${roomId}`);

    // Beri tahu anggota room
    socket.to(roomId).emit('room:user_joined', {
      userId: socket.id,
      roomId,
    });
  });

  // Keluar dari room
  socket.on('leave:room', (roomId) => {
    socket.leave(roomId);
    socket.to(roomId).emit('room:user_left', {
      userId: socket.id,
      roomId,
    });
  });

  // Kirim pesan ke room tertentu
  socket.on('room:message', ({ roomId, message }) => {
    io.to(roomId).emit('room:message', {
      userId: socket.id,
      username: socket.data.user?.name,
      message,
      roomId,
      timestamp: Date.now(),
    });
  });

  // Kirim ke semua room kecuali room tertentu
  socket.on('broadcast:announcement', (data) => {
    socket.broadcast.emit('announcement', data);
    // Atau ke room spesifik:
    // io.to('admin-room').emit('announcement', data);
  });
});

// Dapatkan semua client di room
async function getRoomMembers(roomId: string) {
  const sockets = await io.in(roomId).fetchSockets();
  return sockets.map(s => ({
    id: s.id,
    username: s.data.user?.name,
  }));
}

Namespaces

TypeScript
// Namespace untuk chat
const chatNs = io.of('/chat');
chatNs.on('connection', (socket) => {
  console.log(`Chat namespace: ${socket.id}`);

  socket.on('message', (data) => {
    chatNs.emit('message', data);
  });
});

// Namespace untuk notifications
const notifNs = io.of('/notifications');
notifNs.on('connection', (socket) => {
  console.log(`Notification namespace: ${socket.id}`);

  // Kirim notifikasi ke user tertentu
  socket.emit('notif', {
    title: 'Selamat datang!',
    body: 'Kamu sudah terhubung ke notification service.',
  });
});

// Namespace untuk admin
const adminNs = io.of('/admin');
adminNs.use((socket, next) => {
  // Middleware khusus admin
  if (socket.handshake.auth.role === 'admin') {
    next();
  } else {
    next(new Error('Unauthorized'));
  }
});

// Client: koneksi ke namespace spesifik
// const chatSocket = io('http://localhost:3000/chat');
// const notifSocket = io('http://localhost:3000/notifications');

7. Heartbeat & Reconnection

Dalam aplikasi production, koneksi WebSocket bisa terputus karena berbagai alasan: jaringan tidak stabil, server restart, timeout, atau NAT timeout. Implementasi heartbeat dan reconnection sangat penting untuk pengalaman user yang baik.

Custom Heartbeat

TypeScript
// Server-side heartbeat
const HEARTBEAT_INTERVAL = 30000; // 30 detik
const HEARTBEAT_TIMEOUT = 10000;  // 10 detik timeout

wss.on('connection', (ws) => {
  let isAlive = true;
  let heartbeatTimer: NodeJS.Timeout;

  // Fungsi heartbeat
  function startHeartbeat() {
    heartbeatTimer = setInterval(() => {
      if (!isAlive) {
        console.log('Client tidak merespons, memutus koneksi');
        ws.terminate();
        return;
      }

      isAlive = false;
      ws.ping(); // Kirim ping

      // Tunggu pong
      setTimeout(() => {
        if (!isAlive) {
          ws.terminate();
        }
      }, HEARTBEAT_TIMEOUT);
    }, HEARTBEAT_INTERVAL);
  }

  // Handle pong response
  ws.on('pong', () => {
    isAlive = true;
    console.log(`Pong dari client ${ws._socket.remoteAddress}`);
  });

  ws.on('close', () => {
    clearInterval(heartbeatTimer);
  });

  startHeartbeat();
});

Client-side Reconnection

TypeScript
class WebSocketClient {
  private ws: WebSocket | null = null;
  private url: string;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 10;
  private reconnectDelay = 1000;
  private heartbeatTimer: NodeJS.Timeout | null = null;

  constructor(url: string) {
    this.url = url;
    this.connect();
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      console.log('✅ Terhubung');
      this.reconnectAttempts = 0;
      this.startHeartbeat();
      this.onConnect();
    };

    this.ws.onclose = (event) => {
      console.log(`❌ Terputus: ${event.code}`);
      this.stopHeartbeat();

      if (!event.wasClean && this.reconnectAttempts < this.maxReconnectAttempts) {
        this.reconnect();
      }
    };

    this.ws.onerror = (error) => {
      console.error('Error:', error);
    };

    this.ws.onmessage = (event) => {
      this.handleMessage(event.data);
    };
  }

  // Reconnection dengan exponential backoff
  reconnect() {
    const delay = Math.min(
      this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
      30000 // Max 30 detik
    );

    console.log(`Reconnecting dalam ${delay}ms (attempt ${this.reconnectAttempts + 1})`);

    setTimeout(() => {
      this.reconnectAttempts++;
      this.connect();
    }, delay);
  }

  // Heartbeat: ping setiap 30 detik
  startHeartbeat() {
    this.heartbeatTimer = setInterval(() => {
      if (this.ws?.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify({ type: 'ping' }));
      }
    }, 30000);
  }

  stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
    }
  }

  send(data: unknown) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }

  onConnect() { /* Override */ }
  handleMessage(data: string) { /* Override */ }
}

// Usage
const client = new WebSocketClient('ws://localhost:8080');
client.onConnect = () => console.log('Connected!');
client.handleMessage = (data) => console.log('Message:', data);

8. Real-time Patterns

Pattern 1: Broadcast

TypeScript
// Broadcast: kirim ke semua client
io.on('connection', (socket) => {
  socket.on('broadcast', (message) => {
    // Kirim ke semua termasuk pengirim
    io.emit('broadcast', message);

    // Atau kecuali pengirim
    // socket.broadcast.emit('broadcast', message);
  });
});

Pattern 2: Targeted Messaging

TypeScript
// Private messaging: kirim ke user tertentu
io.on('connection', (socket) => {
  // Simpan mapping userId ke socketId
  const userSockets = new Map<string, string>();

  socket.on('register', (userId) => {
    userSockets.set(userId, socket.id);
  });

  socket.on('private:message', ({ toUserId, message }) => {
    const targetSocketId = userSockets.get(toUserId);

    if (targetSocketId) {
      io.to(targetSocketId).emit('private:message', {
        from: socket.data.user?.name,
        message,
        timestamp: Date.now(),
      });
    }
  });
});

Pattern 3: Presence System

TypeScript
// Presence system: siapa yang online
const onlineUsers = new Map<string, { id: string; name: string; status: string }>();

io.on('connection', (socket) => {
  const user = socket.data.user;

  // Tambahkan ke daftar online
  onlineUsers.set(user.id, {
    id: user.id,
    name: user.name,
    status: 'online',
  });

  // Broadcast status online ke semua
  io.emit('presence:update', {
    userId: user.id,
    status: 'online',
    users: Array.from(onlineUsers.values()),
  });

  // Handle status change
  socket.on('presence:status', (status) => {
    const userData = onlineUsers.get(user.id);
    if (userData) {
      userData.status = status; // 'online', 'away', 'busy'
      io.emit('presence:update', {
        userId: user.id,
        status,
        users: Array.from(onlineUsers.values()),
      });
    }
  });

  // Hapus dari daftar saat disconnect
  socket.on('disconnect', () => {
    onlineUsers.delete(user.id);
    io.emit('presence:update', {
      userId: user.id,
      status: 'offline',
      users: Array.from(onlineUsers.values()),
    });
  });
});

9. Scaling WebSocket

Scaling WebSocket jauh lebih kompleks dibanding scaling HTTP karena koneksi bersifat stateful. Ketika kamu menambah server, kamu perlu memastikan bahwa pesan yang dikirim dari satu server bisa diteruskan ke client yang terhubung di server lain.

Socket.IO Redis Adapter

TypeScript
import { Server } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';

const io = new Server(3000);

// Setup Redis adapter untuk multi-server
const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();

Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
  io.adapter(createAdapter(pubClient, subClient));
  console.log('Redis adapter connected - siap untuk scaling!');
});

// Sekarang io.emit() akan mengirim ke semua client
// di semua server yang terhubung ke Redis yang sama

Load Balancing dengan Nginx

Nginx
# nginx.conf
upstream websocket_backend {
    ip_hash; # Sticky sessions untuk WebSocket
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

server {
    listen 80;
    server_name api.example.com;

    location /socket.io/ {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 86400; # 24 jam
        proxy_send_timeout 86400;
    }
}

Horizontal Scaling Architecture

Text
                    ┌─────────────────┐
                    │   Load Balancer  │
                    │   (Nginx/HAProxy)│
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
        ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
        │  WS Server │ │  WS Server │ │  WS Server │
        │   (3001)   │ │   (3002)   │ │   (3003)   │
        └─────┬─────┘ └─────┬─────┘ └─────┬─────┘
              │              │              │
              └──────────────┼──────────────┘
                             │
                    ┌────────┴────────┐
                    │  Redis Pub/Sub  │
                    │  (Message Bus)  │
                    └─────────────────┘
⚠️ Peringatan

Pastikan menggunakan sticky sessions di load balancer. Tanpa sticky sessions, client bisa terhubung ke server berbeda setiap reconnect, yang menyebabkan masalah state. Socket.IO mendukung ip_hash atau cookie-based affinity.

Scaling Checklist

10. Quiz Pemahaman

Uji pemahamanmu tentang WebSocket:

Pertanyaan 1: Apa keunggulan utama WebSocket dibanding HTTP polling?

a) Lebih aman
b) Koneksi persisten dua arah dengan latency rendah
c) Tidak membutuhkan server
d) Bisa mengirim email

Pertanyaan 2: Apa yang terjadi saat HTTP Upgrade handshake di WebSocket?

a) Browser mengunduh file baru
b) Koneksi HTTP beralih menjadi koneksi WebSocket persisten
c) Server membuat file temporary
d) Browser menutup koneksi lama

Pertanyaan 3: Apa fungsi dari heartbeat di koneksi WebSocket?

a) Mengenkripsi data
b) Memeriksa apakah koneksi masih aktif
c) Mengkompresi data
d) Mengautentikasi user

Pertanyaan 4: Mengapa Redis diperlukan saat scaling WebSocket ke beberapa server?

a) Sebagai database utama
b) Sebagai message bus untuk broadcast pesan lintas server
c) Untuk menyimpan file
d) Untuk caching HTML

Pertanyaan 5: Apa yang dilakukan exponential backoff pada reconnect?

a) Mengurangi interval reconnect secara eksponensial
b) Meningkatkan interval reconnect secara eksponensial untuk menghindari server overload
c) Mengirim data lebih besar setiap reconnect
d) Mengubah protocol setiap reconnect
🔍 Zoom
100%
🎨 Tema