tutorial

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:

  1. Proses upgrade koneksi HTTP→WebSocket.
  2. Registry subscription: mapping antara subscriber (klien) dan event/topic yang mereka minati.
  3. Mekanisme publish: mengirim event/update baru ke subscriber yang relevan.
  4. 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

// main.go
package main

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

// WebSocket upgrader dengan konfigurasi standar
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool { return true },
}

func main() {
    http.HandleFunc("/ws", handleWebSocket)
    log.Println("Listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Handler WebSocket

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()

    // Sini akan register client, baca subscription dll.
    for {
        _, msg, err := conn.ReadMessage()
        if err != nil {
            log.Println("Read error:", err)
            break
        }
        log.Printf("Received: %s", msg)
        // Proses message subscription
    }
}

Registry Subscription

Umumnya, kita perlu struktur data yang mengelola siapa subscribe ke event apa. Misal, klien bisa subscribe ke beberapa device:

type Subscription struct {
    Conn *websocket.Conn
    DeviceIDs map[string]bool // devices yang di-subscribe
}

type Hub struct {
    Subscriptions map[*websocket.Conn]*Subscription
    Register   chan *Subscription
    Unregister chan *Subscription
    Broadcast  chan DeviceEvent
}

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.

type DeviceEvent struct {
    DeviceID string      `json:"device_id"`
    Status   string      `json:"status"`
}

func NewHub() *Hub {
    return &Hub{
        Subscriptions: make(map[*websocket.Conn]*Subscription),
        Register:      make(chan *Subscription),
        Unregister:    make(chan *Subscription),
        Broadcast:     make(chan DeviceEvent, 128),
    }
}

func (h *Hub) Run() {
    for {
        select {
        case s := <-h.Register:
            h.Subscriptions[s.Conn] = s
        case s := <-h.Unregister:
            delete(h.Subscriptions, s.Conn)
        case event := <-h.Broadcast:
            for _, s := range h.Subscriptions {
                if s.DeviceIDs[event.DeviceID] {
                    s.Conn.WriteJSON(event)
                }
            }
        }
    }
}

Update Handler

Setiap ada klien yang mengirim pesan type: "subscribe", kita update subscription klien.

// Di dalam handleWebSocket:
var subscription = &Subscription{
    Conn: conn,
    DeviceIDs: make(map[string]bool),
}
hub.Register <- subscription

for {
    var req struct {
        Type string `json:"type"`
        DeviceID string `json:"device_id"`
    }
    err := conn.ReadJSON(&req)
    if err != nil {
        // handle unregistration...
        hub.Unregister <- subscription
        break
    }
    if req.Type == "subscribe" {
        subscription.DeviceIDs[req.DeviceID] = true
    }
}

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.

func simulateDeviceStatus(hub *Hub) {
    deviceIDs := []string{"dev100", "dev200"}
    statuses := []string{"online", "offline"}
    for {
        for _, id := range deviceIDs {
            status := statuses[time.Now().UnixNano()%2] // acak status
            event := DeviceEvent{
                DeviceID: id,
                Status:   status,
            }
            hub.Broadcast <- event
            time.Sleep(5 * time.Second)
        }
    }
}

Tabel Analisis

FiturPolling HTTPWebSocket+Subscription
LatencyTinggi (interval)Rendah (push langsung)
Bandwidth usageBorosHemat
ScalabilityLumayan sulitLebih mudah (stateful)
Implementasi clientMudahPerlu WS lib
ResilienceRetry manualPerlu handle disconnect

Best-Practices untuk Skala Produksi

  • Heartbeat/Ping: Kirim ping secara berkala untuk mendeteksi koneksi mati.
  • Backpressure: Jangan WriteJSON sembarangan, 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.

comments powered by Disqus