# Membangun Event-Driven Architecture Real-Time dengan WebSockets dan Go

> Diterbitkan oleh Literally Web ID pada 20 Oct 2026
> Sumber: https://literally.web.id/articles/membangun-event-driven-architecture-real-time-dengan-websockets-dan-go

---

Dalam 15 menit ke depan, kita akan merakit sebuah stack komunikasi real-time berkapasitas tinggi dari nol sampai berjalan mulus. Pengalaman saya bertahun-tahun di lapangan membuktikan bahwa menangani jutaan koneksi WebSocket bukanlah tentang seberapa canggih framework yang Anda gunakan, melainkan seberapa cerdas kita mengelola memori dan menjaga agar pipa data tetap bersih dari hambatan.

## 1. Menu Kita Hari Ini & Alat yang Perlu Disiapkan

Pernahkah Anda mendapati server ngos-ngosan ketika ribuan klien terhubung secara bersamaan? Masalah klasik ini sering kali muncul karena kita memperlakukan koneksi WebSocket seperti HTTP biasa yang berumur pendek. Hari ini, kita tidak akan terjebak dalam teori klise. Kita akan langsung membangun arsitektur event-driven menggunakan Go, memanfaatkan goroutine yang sangat ringan, serta mengawinkan Redis Pub/Sub untuk memastikan pesan terdistribusi merata ke seluruh instance server tanpa bottleneck.

Alat tempur yang perlu Anda siapkan di terminal kerja Anda:
- Go (versi 1.21 atau terbaru)
- Redis Server (untuk lapisan Pub/Sub lintas instance)
- Nginx (sebagai reverse proxy dan SSL terminator)
- K6 atau Apache Bench untuk uji beban (Load Testing)

Mari kita pastikan semua bumbu dasar ini siap sebelum kita menyalakan kompor utama.

## 2. Peta Jalan Singkat: Apa yang Sedang Kita Rakit

Bayangkan kita sedang mengelola sebuah gedung konser raksasa. Klien (browser atau aplikasi mobile) adalah penonton yang datang dan menuntut interaksi langsung tanpa jeda. Go bertindak sebagai petugas pintu yang sangat efisien, menangani ratusan ribu penonton secara simultan menggunakan memori yang sangat minim berkat mekanisme goroutine.

Namun, ketika gedung konser kita berkembang menjadi stadion besar dan kita harus menambah jumlah server Go, bagaimana caranya agar penonton di server A bisa mendengar teriakan dari server B? Di sinilah peran Redis Pub/Sub masuk sebagai pengeras suara sentral yang menyatukan seluruh kanal komunikasi.

Transisi mulus dari koneksi tunggal ke klaster terdistribusi ini akan kita bedah langkah demi langkah, dimulai dari fondasi lingkungan kerjanya.

## 3. Langkah 1: Mempersiapkan Fondasi & Environment

Sebelum menulis kode, pastikan sistem operasi Linux Anda (atau WSL jika Anda di Windows) sudah di-tweak untuk menangani file descriptor dalam jumlah besar. Default Linux biasanya membatasi jumlah koneksi terbuka (ulimit -n) sekitar 1024. Nilai itu akan habis seketika saat klien pertama menggebrak server kita.

Buka terminal Anda, buat direktori kerja baru, dan inisialisasi modul Go:

```bash
mkdir -p go-websocket-engine && cd go-websocket-engine
go mod init github.com/username/go-websocket-engine
```

> [!WARNING]
> Jangan pernah melewati konfigurasi `ulimit` di environment production. Jika Anda mengabaikannya, sistem operasi akan melempar error `too many open files` jauh sebelum CPU Anda mencapai setengah kapasitasnya.

Sekarang, mari instal pustaka WebSocket standar industri dan klien Redis resmi untuk Go:

```bash
go get github.com/gorilla/websocket
go get github.com/redis/go-redis/v9
```

Pondasi sudah berdiri tegak. Mari kita lanjutkan ke dapur utama untuk meracik kode intinya.

## 4. Langkah 2: Meracik Konfigurasi Inti Tanpa Basa-basi

Kita buat file `main.go`. Di sinilah keajaiban terjadi. Kita akan merancang sebuah struktur hub sederhana yang mengelola pendaftaran klien, pemutusan koneksi, serta pembacaan dan penulisan payload secara asynchronous menggunakan goroutine.

```go
package main

import (
	"log"
	"net/http"
	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		// Izinkan semua origin untuk keperluan testing lokal
		return true
	},

}

func handleConnections(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("Gagal upgrade koneksi: %v", err)
		return
	}
	defer ws.Close()

	for {
		// Membaca pesan masuk dari klien
		messageType, message, err := ws.ReadMessage()
		ifi err != nil {
			log.Printf("Klien terputus: %v", err)
			break
		}

		log.Printf("Menerima pesan: %s", message)

		// Menggema balik pesan ke klien (Echo test)
		err = ws.WriteMessage(messageType, message)
		ifi err != nil {
			log.Printf("Gagal menulis pesan: %v", err)
			break
		}
	}
}

func main() {
	http.HandleFunc("/ws", handleConnections)
	log.Println("Server WebSocket berjalan di port 8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal("Server error: ", err)
	}
}
```

Jalankan program tersebut dengan perintah `go run main.go`. Anda akan melihat log bahwa server siap melayani di port 8080. Kode ini adalah fondasi awal; nantinya goroutine akan kita skalakan untuk menghandle jutaan event secara paralel.

Namun, bagaimana jika server kita berlipat ganda di balik load balancer Nginx? Mari kita perketat pengamanannya di langkah berikutnya.

## 5. Langkah 3: Sentuhan Akhir Keamanan & Optimasi

Di dunia nyata, koneksi WebSocket yang berjalan di atas protokol HTTP biasa (ws://) sangat rentan terhadap serangan Man-in-the-Middle. Kita wajib mengenkripsikannya menggunakan Transport Layer Security (wss://) via Nginx sebagai reverse proxy.

Berikut adalah konfigurasi Nginx yang sudah di-tuning agar mampu mempertahankan koneksi persistent (keep-alive) tanpa terputus secara sepihak oleh timeout proxy:

```nginx
map $http_upgrade $connection_upgrade {
	default upgrade;
	''      close;
}

upstream websocket_backend {
	server 127.0.0.1:8080;
	keepalive 32;
}

server {
	listen 80;
	server_name real-time.domain.com;
	return 301 https://$host$request_uri;
}

server {
	listen 443 ssl http2;
	server_name real-time.domain.com;

	ssl_certificate /etc/letsencrypt/live/real-time.domain.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/real-time.domain.com/privkey.pem;

	location /ws {
		proxy_pass http://websocket_backend;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection $connection_upgrade;
		proxy_set_header Host $host;
		
		# Timeout yang lebih panjang untuk koneksi WebSocket
		proxy_read_timeout 3600s;
		proxy_send_timeout 3600s;
	}
}
```

> [!TIP]
> Penggunaan variabel `map` di atas sangat krusial agar Nginx secara dinamis meneruskan header `Upgrade` yang dibutuhkan oleh protokol WebSocket.

Konfigurasi ini memastikan lalu lintas data aman, terenkripsi, dan tahan banting terhadap interupsi jaringan publik.

## 6. Tes Rasa: Memastikan Semuanya Berjalan Sempurna

Saatnya menguji racikan kita. Buka terminal terpisah, lalu gunakan perkakas sederhana seperti `websocat` atau skrip kecil JavaScript di browser untuk melakukan koneksi uji coba.

```bash
# Pastikan websocat terinstall, lalu tembak endpoint lokal
websocat ws://localhost:8080/ws
```

Ketikkan sebuah kalimat, misalnya: `Halo Arsitek!`. Jika terminal server Go mencetak log `Menerima pesan: Halo Arsitek!` dan klien Anda menerima balasan yang sama secara instan, selamat! Resep kita berhasil sempurna tanpa ada cacat sintaks.

## 7. Pertolongan Pertama: Jika Terjadi Kendala Kecil

Di lapangan, Anda mungkin akan menemui beberapa hambatan kecil:
- **Koneksi terputus tiba-tiba setelah 60 detik**: Ini biasanya karena timeout dari Nginx atau load balancer perantara. Pastikan direktif `proxy_read_timeout` di Nginx sudah diset ke nilai yang tinggi.
- **Error Out of Memory saat beban tinggi**: Periksa alokasi buffer pembacaan/penulisan (`ReadBufferSize`). Jangan alokasikan buffer terlalu besar per klien jika payload rata-rata berukuran kecil.

Dengan arsitektur dasar Go dan Redis Pub/Sub ini, Anda kini memiliki pondasi kokoh untuk membangun sistem chat, dashboard finansial live, atau kolaborasi dokumen real-time berkapasitas masif.