08/05/2026 06:52น.

EP.110 การทำ Auto-Scaling และ Load Balancing ขั้นสูง สำหรับ WebSocket Server
#WebSocket Production
#Go
#Load Balancing
#Auto-Scaling
#WebSocket
เมื่อจำนวนผู้ใช้ WebSocket ของคุณเติบโตจาก "หลักร้อย" ไปสู่ "หลักหมื่น" คุณจะเริ่มพบกับปัญหาด้านโหลด การเชื่อมต่อซ้ำซ้อน และการกระจายทราฟฟิกที่ไม่สมดุลทันที ⚡
EP นี้จะพาคุณมาเรียนรู้การออกแบบ WebSocket Server ที่สามารถขยายตัวได้อัตโนมัติ (Auto-Scaling) และ กระจายโหลดได้อย่างมีประสิทธิภาพ (Load Balancing) โดยใช้เครื่องมือระดับ Production เช่น Kubernetes, Load Balancer, Sticky Session และ Redis Pub/Sub
🧩 1. ทำไมต้อง Auto-Scaling และ Load Balancing?
| ปัญหา | คำอธิบาย |
|---|---|
| Connection เกิน limit | Server เดียวรับโหลดไม่ไหว |
| ผู้ใช้ reconnect บ่อย | การกระจายโหลดไม่สมดุล |
| Message สูญหาย | ขาดการสื่อสารระหว่าง WebSocket instance |
| Latency สูง | ทราฟฟิกไม่ถูกส่งไปยัง node ที่ใกล้ที่สุด |
ระบบระดับ Production ที่รองรับผู้ใช้หลายหมื่นคนต้องสามารถ “เพิ่ม–ลดขนาด” ได้อัตโนมัติตามปริมาณโหลด
⚙️ 2. สถาปัตยกรรมระบบ Auto-Scaling WebSocket
Client
↓
Load Balancer (Sticky Session)
↓
WebSocket Server Pods (Kubernetes)
↓
Redis Pub/Sub (Message Sync)
↓
Database / Services
Key Component:
- Load Balancer → กระจายผู้ใช้ไปยัง Pod ที่เหมาะสม
- Sticky Session → รักษาการเชื่อมต่อของผู้ใช้กับ Pod เดิม
- Redis Pub/Sub → ส่งข้อความข้าม instance แบบเรียลไทม์
🧠 3. ตัวอย่างโค้ด: Redis Pub/Sub Sync Messages
// ติดตั้ง redis & gorilla/websocket ก่อน
// go get github.com/redis/go-redis/v9
// go get github.com/gorilla/websocket
var (
upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
rdb = redis.NewClient(&redis.Options{Addr: "localhost:6379"})
ctx = context.Background()
clients = make(map[*websocket.Conn]bool)
)
func handleConnections(w http.ResponseWriter, r *http.Request) {
conn, _ := upgrader.Upgrade(w, r, nil)
defer conn.Close()
clients[conn] = true
defer delete(clients, conn)
for {
_, msg, err := conn.ReadMessage()
if err != nil {
break
}
// Publish message to Redis channel
rdb.Publish(ctx, "chat_channel", msg)
}
}
func handleRedisMessages() {
subscriber := rdb.Subscribe(ctx, "chat_channel")
ch := subscriber.Channel()
for msg := range ch {
for client := range clients {
client.WriteMessage(websocket.TextMessage, []byte(msg.Payload))
}
}
}
func main() {
go handleRedisMessages()
http.HandleFunc("/ws", handleConnections)
log.Println("WebSocket Server running on :8080")
http.ListenAndServe(":8080", nil)
}
✅ ทุกข้อความที่ถูกส่งจาก client ใด ๆ จะถูก publish เข้า Redis และ broadcast ไปยังทุก instance พร้อมกัน
🧩 4. Load Balancer + Sticky Session
Sticky Session มีความสำคัญอย่างยิ่งสำหรับระบบ multi-instance เพราะช่วยให้ผู้ใช้เชื่อมต่อกับ Pod เดิมได้อย่างต่อเนื่อง
ตัวอย่างการตั้งค่า Nginx Ingress:
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
👉 ทำให้ connection คงที่ แม้มีการโหลดสูง ลดปัญหา reconnect และ message loss
☁️ 5. การตั้งค่า Auto-Scaling บน Kubernetes
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: websocket-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: websocket-deployment
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
💡 เมื่อ CPU ใช้เกิน 70% → ระบบจะเพิ่ม Pod ใหม่โดยอัตโนมัติ
🧠 6. Best Practices
| หมวด | แนวทาง |
|---|---|
| Scaling | ใช้ Horizontal Scaling (K8s HPA) |
| Load Balancing | กำหนด Sticky Session อย่างเหมาะสม |
| Messaging | ใช้ Redis Pub/Sub เพื่อ sync message |
| Monitoring | ใช้ Prometheus + Grafana เพื่อติดตาม Resource |
| Resilience | มี reconnect logic และ graceful shutdown เสมอ |
🚀 ท้าให้ลอง!
- สร้าง WebSocket Server 2 instance
- เชื่อมต่อทั้งคู่กับ Redis Pub/Sub
- เปิด 2 browser หรือ 2 เครื่อง แล้วส่งข้อความจากฝั่งหนึ่ง
✅ ถ้าข้อความส่งถึงทั้งสองฝั่งทันที → ยินดีด้วย! คุณกำลังก้าวเข้าสู่โครงสร้างระบบแบบ Production ที่แท้จริง
🌟 EP ถัดไป
📘 EP.111: การจัดการ Message Ordering และ Event Sequence
เรียนรู้วิธีจัดลำดับข้อความใน WebSocket ให้ “ไม่หลุด” แม้ในระบบที่มีการเชื่อมต่อพร้อมกันจำนวนมาก! เพื่อให้ผู้ใช้ทุกคนได้รับ Event แบบเรียงลำดับ 100% อย่างแม่นยำ