Kenapa WebSocket untuk SaaS?
Real-time experience adalah expectation sekarang. User mau notifikasi instant, live dashboard, dan collaborative editing. WebSocket adalah protocol yang tepat untuk ini full-duplex, low latency, dan efficient.
Saya sudah implement WebSocket di beberapa produk: live chat support, real-time analytics dashboard, dan collaborative project management tool.
Setup WebSocket Server di Go
package handlers
import (
"log"
"sync"
"github.com/gofiber/websocket/v2"
"github.com/gofiber/fiber/v2"
)
type Client struct {
Conn *websocket.Conn
ID string
Send chan []byte
}
type Hub struct {
Clients map[*Client]bool
Broadcast chan []byte
Register chan *Client
Unregister chan *Client
mu sync.RWMutex
}
func NewHub() *Hub {
return &Hub{
Clients: make(map[*Client]bool),
Broadcast: make(chan []byte, 256),
Register: make(chan *Client),
Unregister: make(chan *Client),
}
}
func (h *Hub) Run() {
for {
select {
case client := <-h.Register:
h.mu.Lock()
h.Clients[client] = true
h.mu.Unlock()
log.Printf("Client connected: %s", client.ID)
case client := <-h.Unregister:
h.mu.Lock()
if _, ok := h.Clients[client]; ok {
delete(h.Clients, client)
close(client.Send)
}
h.mu.Unlock()
case message := <-h.Broadcast:
h.mu.RLock()
for client := range h.Clients {
select {
case client.Send <- message:
default:
close(client.Send)
delete(h.Clients, client)
}
}
h.mu.RUnlock()
}
}
}
Fiber WebSocket Handler
func SetupWebSocketRoutes(app *fiber.App, hub *Hub) {
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) { return c.Next() }
return c.SendStatus(fiber.StatusUpgradeRequired)
})
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
client := &Client{
Conn: c, ID: uuid.New().String(),
Send: make(chan []byte, 256),
}
hub.Register <- client
go func() {
defer c.Close()
for message := range client.Send {
if err := c.WriteMessage(websocket.TextMessage, message); err != nil {
return
}
}
}()
for {
_, message, err := c.ReadMessage()
if err != nil { break }
hub.Broadcast <- message
}
hub.Unregister <- client
}))
}
Real-time Notifications
func (h *Hub) SendToUser(userID string, payload Notification) {
data, _ := json.Marshal(payload)
h.mu.RLock()
defer h.mu.RUnlock()
for client := range h.Clients {
if client.UserID == userID {
select {
case client.Send <- data:
default: // Client too slow, skip
}
}
}
}
Performance Tips
- Connection pooling Batasi jumlah connections per client (misal: 3 max)
- Heartbeat/Ping Kirim ping setiap 30 detik untuk detect dead connections
- Compression Enable permessage-deflate untuk payload besar
- Message size limit Set max message size untuk prevent abuse
WebSocket di Go sangat performant karena goroutine model-nya. Saya pernah handle 50K+ concurrent WebSocket connections di satu server. Untuk scale lebih besar, tambahkan Redis Pub/Sub sebagai message broker antar instances.
Scaling WebSocket dengan Redis Pub/Sub
Ketika SaaS Anda scale ke beberapa server instances, WebSocket messages harus bisa dikirim lintas instances. Redis Pub/Sub adalah solusi yang simple dan efektif:
import "github.com/redis/go-redis/v9"
func (h *Hub) SetupRedisPubSub(rdb *redis.Client) {
pubsub := rdb.Subscribe(context.Background(), "ws:broadcast")
ch := pubsub.Channel()
go func() {
for msg := range ch {
var event WSEvent
json.Unmarshal([]byte(msg.Payload), &event)
for client := range h.Clients {
if client.UserID == event.UserID {
client.Send <- []byte(event.Data)
}
}
}
}()
}
Dengan Redis Pub/Sub, semua instances akan menerima message yang sama. Setiap instance hanya mengirim ke local clients yang terhubung. Simple, scalable, dan battle-tested.
Connection Monitoring dan Alerting
Setup monitoring untuk WebSocket connections. Metrics yang perlu di-track:
- Total connections berapa banyak active WebSocket connections
- Messages per second throughput message untuk capacity planning
- Connection duration berapa lama rata-rata client terhubung
- Error rate frequency of connection drops dan errors
Gunakan Prometheus metrics exporter untuk collect data ini, lalu visualize di Grafana dashboard. Alert jika connection count atau error rate melampaui threshold tertentu.