Skip to main content
BlogBackend

Server-Sent Events vs WebSockets untuk Realtime

Kapan SSE satu arah mengalahkan WebSocket penuh, bagaimana reconnection dan HTTP/2 mengubah perhitungan, dan cara saya memilih per use case.

Matthews Wong

Software Engineer

Juli 20269 menit baca
Server-Sent Events vs WebSockets untuk Realtime

Foto oleh Blackcat via Wikimedia Commons (CC BY-SA 4.0)

Ringkasan Utama

Pakai Server-Sent Events ketika server hanya perlu mendorong data ke klien: berjalan di atas HTTP biasa, reconnect otomatis, dan ter-multiplex rapi di HTTP/2. Gunakan WebSockets saat klien juga harus mengirim aliran data balik, seperti chat, editing kolaboratif, atau game, di mana aliran dua arah penuh sepadan dengan protokol tambahannya.

Setiap beberapa bulan sebuah proyek meminta fitur realtime dan seseorang langsung meraih WebSockets. Sering kali itu berlebihan. Sebagian besar fitur realtime bersifat satu arah: dashboard yang diperbarui, progress bar untuk job panjang, notifikasi, live log, atau model AI yang men-streaming token. Untuk semua itu, Server-Sent Events menyelesaikan tugas dengan kode jauh lebih sedikit dan beban operasional jauh lebih ringan.

Saya menjalankan beberapa layanan di VPS self-hosted di belakang reverse proxy, dan pilihan antara dua transport ini punya konsekuensi nyata terhadap berapa banyak logika reconnection yang harus saya tulis, bagaimana proxy berperilaku, dan berapa banyak koneksi yang bahkan diizinkan oleh browser. Berikut cara saya menalarnya dalam praktik, dengan trade-off yang benar-benar penting.

Satu arah vs dua arah: pembeda utama

Server-Sent Events bersifat satu arah secara desain. Klien membuka satu request HTTP berumur panjang dan server men-streaming aliran teks event ke bawahnya, terus-menerus, sampai ada yang menutupnya. Klien tidak bisa mengirim data balik lewat kanal yang sama; untuk berbicara ke server ia membuat request HTTP biasa di samping stream itu. Sebaliknya, WebSockets meng-upgrade koneksi menjadi protokol full-duplex di mana kedua sisi mendorong frame kapan pun mereka mau.

Perbedaan tunggal itu menentukan sebagian besar kasus. Jika interaksinya benar-benar percakapan, dengan klien memancarkan pesan terus-menerus, WebSockets cocok secara alami. Jika klien sebagian besar hanya pendengar yang sesekali membuat request biasa, SSE adalah model yang lebih sederhana dan lebih jujur. Memaksakan pipa dua arah pada masalah satu arah hanya menambah permukaan yang harus diamankan dan dipelihara.

Reconnection: SSE memberikannya secara gratis

Ini adalah fitur SSE yang paling diremehkan orang. API EventSource di browser reconnect otomatis saat koneksi putus, dengan retry default sekitar tiga detik yang bisa diubah server dengan mengirim field retry di dalam stream. Lebih bagus lagi, jika Anda melampirkan id pada setiap event, browser menyimpannya dan mengirim header Last-Event-ID saat reconnect, sehingga server bisa melanjutkan tepat dari titik terakhir alih-alih memutar ulang atau menjatuhkan pesan.

// SSE stream — reconnection and resume are built in
// Client:
const es = new EventSource("/api/updates");
es.onmessage = (e) => console.log("data:", e.data);
es.onerror = () => console.log("reconnecting automatically...");

// Server (Node/Express) response body format:
//   id: 42\n
//   retry: 5000\n
//   data: {"progress":73}\n\n
// On reconnect the browser sends:  Last-Event-ID: 42
res.writeHead(200, {
  "Content-Type": "text/event-stream",
  "Cache-Control": "no-cache",
  "Connection": "keep-alive",
});
res.write(`id: ${seq}\nretry: 5000\ndata: ${JSON.stringify(payload)}\n\n`);

WebSockets tidak punya semua ini. Saat WebSocket tertutup, klien hanya diam; Anda harus menulis sendiri loop reconnect, backoff, dan logika replay pesan, atau memakai library yang melakukannya. Bagi banyak tim biaya tersembunyi itu menjadi faktor penentu: SSE menghadirkan perilaku tangguh yang jika tidak, akan Anda implementasikan ulang, dengan buruk, di bawah tenggat waktu.

Perilaku proxy dan HTTP/2

Karena SSE hanyalah response HTTP panjang, ia biasanya lewat melalui infrastruktur yang ada, load balancer, middleware auth, dan CDN, tanpa perlakuan khusus. WebSockets butuh handshake HTTP Upgrade eksplisit, dan setiap proxy di jalur harus dikonfigurasi untuk mengizinkan dan meneruskannya. Itu lebih banyak bagian bergerak yang harus dibereskan.

SSE punya satu jebakan klasik di belakang reverse proxy: buffering response. Nginx mem-buffer response yang di-proxy secara default, jadi ia menahan event Anda sampai buffer penuh alih-alih mem-flush satu per satu. Anda harus menonaktifkan buffering dan menaikkan read timeout, atau stream realtime Anda tiba dalam batch yang senyap.

# Nginx: SSE endpoint behind a reverse proxy
location /api/updates {
    proxy_pass http://app_upstream;
    proxy_http_version 1.1;
    proxy_set_header Connection "";     # keep upstream alive
    proxy_buffering off;                # critical: flush each event
    proxy_cache off;
    proxy_read_timeout 86400s;          # do not kill idle-ish streams
    add_header X-Accel-Buffering no;    # override buffering explicitly
}

# WebSocket endpoint needs the Upgrade dance instead:
location /ws {
    proxy_pass http://app_upstream;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Keluhan lama terhadap SSE adalah batas koneksi HTTP/1.1: browser hanya mengizinkan sekitar enam koneksi bersamaan per origin, dan setiap stream SSE memakan satu, yang bisa membuat sisa halaman kelaparan di beberapa tab. HTTP/2 menghapus batasan itu dengan mem-multiplex banyak stream di atas satu koneksi TCP, dengan default hasil negosiasi sekitar seratus stream bersamaan. Jadi di deployment HTTP/2 modern mana pun, batasan itu praktis hilang, yang menjadi alasan besar SSE menjadi transport default bagi SDK AI yang men-streaming token model.

Perbandingan sekilas

DimensiServer-Sent EventsWebSockets
ArahServer ke klien sajaDua arah penuh
ProtokolHTTP biasa, text/event-streamProtokol terpisah lewat HTTP Upgrade
ReconnectionOtomatis, dengan resume Last-Event-IDManual, Anda bangun backoff dan replay
Keramahan proxyLewat sebagai HTTP biasa, awasi bufferingButuh konfigurasi Upgrade di tiap hop
Perilaku HTTP/2Multiplex, batas per-origin hilangTanpa multiplexing bawaan, TCP sendiri
Payload dataHanya teks UTF-8Frame teks dan biner

Cara saya memilih per use case

  • Dashboard live, metrik, progress bar, notifikasi, activity feed: SSE. Push satu arah, reconnect otomatis, tak ada yang perlu dibuat manual.
  • Streaming token AI dan LLM: SSE. Ia menjadi transport default SDK AI besar justru karena alirannya server ke klien dengan multiplexing HTTP/2 yang rapi.
  • Chat, editing kolaboratif, game multiplayer, kursor live: WebSockets. Klien memancarkan aliran terus-menerus, jadi full duplex layak dipakai.
  • Input klien biner atau frekuensi tinggi, seperti audio atau upload telemetri: WebSockets. SSE hanya membawa teks UTF-8 satu arah.
  • Ragu dan sisi klien mayoritas membaca: mulai dengan SSE. Lebih murah menambah endpoint POST biasa nanti daripada mengoperasikan socket dua arah yang tak Anda butuhkan.

Hibrida yang berguna: streaming update server lewat SSE dan kirim aksi klien sesekali lewat HTTP POST biasa. Anda mendapat push plus reconnection gratis sambil menjaga penanganan request di HTTP yang familiar, bisa di-cache, dan ramah auth. Naik ke WebSockets hanya saat obrolan klien-ke-server menjadi konstan.

Kesalahan yang paling sering saya lihat adalah meraih alat yang lebih powerful secara refleks. WebSockets memang lebih baik saat Anda membutuhkannya, tapi ia membawa biaya berkelanjutan: kode reconnection, konfigurasi proxy, dan protokol yang berada di luar tooling HTTP normal Anda. SSE diam-diam menyelesaikan kasus umum. Cocokkan transport dengan arah data Anda yang sebenarnya, dan sebagian besar fitur realtime jadi lebih sederhana.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa perbedaan utama antara Server-Sent Events dan WebSockets?

Server-Sent Events bersifat satu arah: server men-streaming data ke klien lewat request HTTP berumur panjang, dan klien tidak bisa mengirim data balik lewat kanal yang sama. WebSockets bersifat dua arah dan full-duplex, sehingga klien dan server bisa mendorong pesan kapan saja. Arah aliran data itulah faktor utama dalam memilih di antara keduanya.

Apakah Server-Sent Events reconnect otomatis?

Ya. API EventSource di browser reconnect otomatis saat koneksi putus, dengan interval retry default sekitar tiga detik yang bisa ditimpa server lewat field retry. Jika setiap event membawa id, browser mengirim header Last-Event-ID saat reconnect sehingga server bisa melanjutkan dari titik berhenti. WebSockets tidak punya reconnection bawaan, jadi Anda harus menulis logika itu sendiri.

Apakah batas enam koneksi browser masih memengaruhi SSE?

Hanya di HTTP/1.1. Browser membatasi koneksi bersamaan per origin sekitar enam, dan setiap stream SSE memakan satu, yang bisa membuat halaman dengan banyak tab kelaparan. HTTP/2 mem-multiplex banyak stream di atas satu koneksi TCP, dengan default hasil negosiasi sekitar seratus, sehingga batasan itu praktis hilang di deployment HTTP/2 modern.

Mengapa stream SSE putus di belakang Nginx atau reverse proxy?

Penyebab umumnya adalah buffering response. Nginx mem-buffer response yang di-proxy secara default, menahan event sampai buffer penuh alih-alih mem-flush satu per satu segera. Menyetel proxy_buffering off, menaikkan proxy_read_timeout, dan menambah header X-Accel-Buffering no memulihkan pengiriman real-time. WebSockets sebaliknya butuh proxy meneruskan handshake HTTP Upgrade.

Sebaiknya pakai SSE atau WebSockets untuk streaming token AI?

Server-Sent Events adalah pilihan umum untuk streaming token AI atau LLM karena alirannya murni server ke klien dan ter-multiplex rapi di HTTP/2. Sebagian besar SDK AI besar default ke SSE justru karena alasan ini. Gunakan WebSockets hanya jika klien juga perlu men-streaming input terus-menerus balik ke server selama generasi.