58 Implementasi Subscription dengan gorilla/websocket
58 Implementasi Subscription dengan gorilla/websocket
Di dunia modern aplikasi web interaktif, notifikasi real-time menjadi kebutuhan wajib. Mulai dari chat, kolaborasi dokumen, hingga update harga saham — semuanya membutuhkan kemampuan server untuk “mendorong” data ke klien secara real-time. Salah satu pola populer untuk menyelesaikan ini adalah Subscription, di mana klien berlangganan pada stream data dan server mengirim update baru ke klien secara langsung.
Pada artikel ini, saya akan membahas secara mendalam bagaimana mengimplementasikan mekanisme subscription menggunakan library gorilla/websocket di Go, yang telah menjadi de-facto standard untuk komunikasi WebSocket di ekosistem Go. Kita akan membahas arsitektur, struktur kode, simulasi alur data, beserta contoh kode nyata dan tips produksi.
Apa itu Subscription?
Sederhananya, subscription adalah model komunikasi di mana klien mendaftarkan minatnya ke event/stream tertentu dan akan menerima update dari server ketika terjadi perubahan. Kebalikannya polling berkala, di mana klien harus meminta informasi baru secara rutin, subscription lebih efisien karena hanya push data baru ketika dibutuhkan.
Studi Kasus
Sebagai contoh, anggap kita memiliki aplikasi monitoring device. Setiap device mengirimkan status secara periodik ke server backend, dan pengguna ingin menerima update real-time jika device status berubah (online/offline, error, dsb).
WebSocket dengan gorilla/websocket di Go
gorilla/websocket menyediakan API sederhana untuk upgrade koneksi HTTP konvensional menjadi WebSocket yang full-duplex, dan cocok untuk use case subscription berbasis event seperti ini.
Secara umum, implementasi subscription real-time memerlukan:
- Proses upgrade koneksi HTTP→WebSocket.
- Registry subscription: mapping antara subscriber (klien) dan event/topic yang mereka minati.
- Mekanisme publish: mengirim event/update baru ke subscriber yang relevan.
- Manajemen koneksi — misal deteksi disconnect, retry, dsb.
Mari kita bedah satu per satu.
Diagram Arsitektur
flowchart LR
subgraph Server
U["HTTP/WS Upgrade"] --> R["Registry Subscription"]
R -->|Device Event| P["Publish Event"]
P -.->|Push| WSClient1((Client 1))
P -.->|Push| WSClient2((Client 2))
end
Device["Updating Device Status"] --> Server
Penjelasan:
- Device melakukan push status ke backend.
- Server menerima dan memproses, lalu meneruskan update ke klien yang telah subscribe melalui WebSocket.
Implementasi Dasar Subscription
Mari kita mulai dengan gigitan kecil: bagaimana menyiapkan server Go yang menerima koneksi WebSocket.
Inisialisasi Handler
1// main.go
2package main
3
4import (
5 "log"
6 "net/http"
7 "github.com/gorilla/websocket"
8)
9
10// WebSocket upgrader dengan konfigurasi standar
11var upgrader = websocket.Upgrader{
12 ReadBufferSize: 1024,
13 WriteBufferSize: 1024,
14 CheckOrigin: func(r *http.Request) bool { return true },
15}
16
17func main() {
18 http.HandleFunc("/ws", handleWebSocket)
19 log.Println("Listening on :8080")
20 log.Fatal(http.ListenAndServe(":8080", nil))
21}Handler WebSocket
1func handleWebSocket(w http.ResponseWriter, r *http.Request) {
2 conn, err := upgrader.Upgrade(w, r, nil)
3 if err != nil {
4 log.Println("Upgrade error:", err)
5 return
6 }
7 defer conn.Close()
8
9 // Sini akan register client, baca subscription dll.
10 for {
11 _, msg, err := conn.ReadMessage()
12 if err != nil {
13 log.Println("Read error:", err)
14 break
15 }
16 log.Printf("Received: %s", msg)
17 // Proses message subscription
18 }
19}Registry Subscription
Umumnya, kita perlu struktur data yang mengelola siapa subscribe ke event apa. Misal, klien bisa subscribe ke beberapa device:
1type Subscription struct {
2 Conn *websocket.Conn
3 DeviceIDs map[string]bool // devices yang di-subscribe
4}
5
6type Hub struct {
7 Subscriptions map[*websocket.Conn]*Subscription
8 Register chan *Subscription
9 Unregister chan *Subscription
10 Broadcast chan DeviceEvent
11}Penjelasan:
- Hub adalah pusat dari semua subscription, mengelola pendaftaran/penghapusan, serta broadcast event.
- Subscription garisnya satu klien + info subscription-nya.
Proses Workflow Subscription
Mari kita lihat workflow subscription tahap per tahap.
sequenceDiagram
participant Client
participant Server
participant Device
Client->>Server: Requests /ws (Upgrade to WS)
Server->>Client: WebSocket Connection Established
Client->>Server: { "type": "subscribe", "device_id": "dev123" }
Device->>Server: Send status update for dev123
Server->>Client: Push event update (if subscribed)
Implementasi Lengkap Hub
Mari kita lengkapi hub yang bisa mendaftarkan subscription serta men-dispatch event.
1type DeviceEvent struct {
2 DeviceID string `json:"device_id"`
3 Status string `json:"status"`
4}
5
6func NewHub() *Hub {
7 return &Hub{
8 Subscriptions: make(map[*websocket.Conn]*Subscription),
9 Register: make(chan *Subscription),
10 Unregister: make(chan *Subscription),
11 Broadcast: make(chan DeviceEvent, 128),
12 }
13}
14
15func (h *Hub) Run() {
16 for {
17 select {
18 case s := <-h.Register:
19 h.Subscriptions[s.Conn] = s
20 case s := <-h.Unregister:
21 delete(h.Subscriptions, s.Conn)
22 case event := <-h.Broadcast:
23 for _, s := range h.Subscriptions {
24 if s.DeviceIDs[event.DeviceID] {
25 s.Conn.WriteJSON(event)
26 }
27 }
28 }
29 }
30}Update Handler
Setiap ada klien yang mengirim pesan type: "subscribe", kita update subscription klien.
1// Di dalam handleWebSocket:
2var subscription = &Subscription{
3 Conn: conn,
4 DeviceIDs: make(map[string]bool),
5}
6hub.Register <- subscription
7
8for {
9 var req struct {
10 Type string `json:"type"`
11 DeviceID string `json:"device_id"`
12 }
13 err := conn.ReadJSON(&req)
14 if err != nil {
15 // handle unregistration...
16 hub.Unregister <- subscription
17 break
18 }
19 if req.Type == "subscribe" {
20 subscription.DeviceIDs[req.DeviceID] = true
21 }
22}Simulasi Push Event dari Device
Di aplikasi nyata, push dari device dilakukan dengan MQTT, HTTP, atau gRPC. Di artikel ini, kita simulasi push event via channel.
1func simulateDeviceStatus(hub *Hub) {
2 deviceIDs := []string{"dev100", "dev200"}
3 statuses := []string{"online", "offline"}
4 for {
5 for _, id := range deviceIDs {
6 status := statuses[time.Now().UnixNano()%2] // acak status
7 event := DeviceEvent{
8 DeviceID: id,
9 Status: status,
10 }
11 hub.Broadcast <- event
12 time.Sleep(5 * time.Second)
13 }
14 }
15}Tabel Analisis
| Fitur | Polling HTTP | WebSocket+Subscription |
|---|---|---|
| Latency | Tinggi (interval) | Rendah (push langsung) |
| Bandwidth usage | Boros | Hemat |
| Scalability | Lumayan sulit | Lebih mudah (stateful) |
| Implementasi client | Mudah | Perlu WS lib |
| Resilience | Retry manual | Perlu handle disconnect |
Best-Practices untuk Skala Produksi
- Heartbeat/Ping: Kirim ping secara berkala untuk mendeteksi koneksi mati.
- Backpressure: Jangan
WriteJSONsembarangan, tangani kelambatan klien (buffered channel). - Security: Batasi origin, gunakan Auth (JWT/OAuth), validasi subscription.
- Unregister: Pastikan setiap klien unregister di server ketika disconnect/koneksi error.
- Horizontal Scaling: Untuk scale-out, gunakan shared queue (misal Redis PubSub) antar instance server.
Penutup
Dengan gorilla/websocket, membangun servis subscription real-time sangat feasible dan clean. Pola Hub seperti di atas sangat cocok di dunia event-driven dan bisa dikembangkan menjadi infra-realtime skala medium besar (untuk skala besar bisa bahas teknologi tambahan, misal message broker/distributed queue).
Membawa notifikasi push ke aplikasi Go Anda kini sangat achievable — dan subscription bisa jadi kekuatan aplikasi Anda berikutnya.
Repo Implementasi contoh:
https://github.com/gorilla/websocket
(official)
https://github.com/your-github/real-time-device-subscription
(simulasi artikel¹)
¹ Contoh tidak nyata, silakan implementasikan versi dasar di atas.
Terima kasih telah membaca!
Jika bermanfaat, silakan share atau tinggalkan komentar seputar kesulitan implementasi subscription real-time di projek Go Anda.