การดู : 0

12/04/2026 18:16น.

EP.123 Load Balancing & Sticky Sessions สำหรับ WebSocket (Production-Ready Guide)

EP.123 Load Balancing & Sticky Sessions สำหรับ WebSocket (Production-Ready Guide)

#WebSocket Server

#Go

#Kubernetes

#Real-time System

#Golang

เมื่อ WebSocket Server ของคุณเริ่มมีผู้ใช้งานจำนวนมา การเพิ่ม Server หรือ Pod อย่างเดียว ไม่เพียงพอ

 

ปัญหาที่มักเกิดขึ้นในระบบจริง ได้แก่

  • ผู้ใช้เชื่อมต่อแล้วหลุดกลางทาง
  • Message ส่งไม่ถึงหรือมาช้า
  • Client reconnect วนจนระบบ overload

 

สาเหตุหลักแทบทุกครั้งมาจาก 👉 Load Balancer ที่ไม่เข้าใจ WebSocket และไม่มี Sticky Session

 

บทความนี้จะพาคุณเข้าใจตั้งแต่ แนวคิดพื้นฐาน → วิธีใช้งานจริง → Best Practices ระดับ Production

 

🎯 เป้าหมายของบทความนี้

 

หลังอ่านจบ คุณจะเข้าใจว่า

  • ทำไม WebSocket ต้องใช้ Sticky Session
  • Load Balancer แบบไหนรองรับ WebSocket ได้จริง
  • วิธี Scale ระบบโดยไม่ทำให้ผู้ใช้หลุด
  • แนวคิดออกแบบ WebSocket Server ให้พร้อม Production

 

🧠 ทำไม WebSocket “ไม่เหมือน” HTTP

 

HTTP (Stateless)

  • Request → Response → จบ
  • Load Balancer กระจาย request ได้อิสระ
  • เปลี่ยน Server ทุกครั้งไม่กระทบผู้ใช้

 

WebSocket (Stateful)

  • Connect ครั้งเดียว → ค้างยาว
  • Connection ผูกกับ Server ตัวเดียว
  • ถ้า request ถูกส่งไป Server อื่น → ❌ connection หลุดทันที

 

🔑 WebSocket ต้องอยู่กับ Server เดิมตลอดอายุการเชื่อมต่อ

 

🍪 Sticky Session คืออะไร?

 

Sticky Session คือการบอก Load Balancer ว่า

“Client คนนี้ ต้องถูกส่งไป Server ตัวเดิมเสมอ”

 

วิธีทำ Sticky Session ที่ใช้กันจริง

  • Cookie-based
  • IP Hash
  • Header-based

 

👉 สำหรับ WebSocket: ต้องมี Sticky Session เสมอ

 

❌ จะเกิดอะไรถ้า “ไม่มี” Sticky Session

 

สถานการณ์จริงที่พบบ่อย

  1. Client เชื่อมต่อ WebSocket ผ่าน Load Balancer
  2. LB ส่งไป Pod A
  3. ระบบ Scale เพิ่ม Pod B
  4. Packet ถัดไปถูกส่งไป Pod B
  5. ❌ WebSocket connection พังทันที

 

ผลลัพธ์ที่ผู้ใช้เห็นคือ

  • หลุดเอง
  • Reconnect วน
  • Message หาย

 

⚖️ ประเภท Load Balancer ที่ใช้งานกับ WebSocket ได้จริง

 

1. Layer 4 Load Balancer (TCP)

เหมาะกับ WebSocket มากที่สุด

 

ตัวอย่าง

  • AWS NLB
  • GCP TCP Load Balancer
  • HAProxy (TCP mode)

 

ข้อดี

  • ไม่แตะ protocol
  • Connection คงที่
  • Sticky โดยธรรมชาติ

 

2. Layer 7 Load Balancer (HTTP)

ใช้ได้ แต่ต้องตั้งค่าให้ถูก

 

ตัวอย่าง

  • Nginx
  • AWS ALB
  • Traefik

 

สิ่งที่ต้องมี

  • รองรับ Upgrade: websocket
  • เปิด Sticky Session
  • Timeout ต้องยาวพอ (ไม่ต่ำกว่า 1–2 ชั่วโมง)

 

⚙️ ตัวอย่าง Sticky Session (Nginx)

 

upstream websocket_backend {
    ip_hash;
    server ws1:8080;
    server ws2:8080;
}

server {
    location /ws {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_read_timeout 1h;
    }
}

 

ip_hash คือ Sticky Session แบบง่าย ใช้ได้ดีในหลายกรณี

 

📦 Sticky Session บน Kubernetes

 

แนวทางที่ใช้จริง

1. Service + Session Affinity

spec:
  sessionAffinity: ClientIP

 

2. Ingress (Nginx Ingress)

nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "ws-session"

 

🔧 ตัวอย่าง WebSocket Server ด้วย Go (Production-friendly)

 

package main

import (
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func wsHandler(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()

	for {
		msgType, msg, err := conn.ReadMessage()
		if err != nil {
			log.Println("read error:", err)
			break
		}

		err = conn.WriteMessage(msgType, msg)
		if err != nil {
			log.Println("write error:", err)
			break
		}
	}
}

func main() {
	http.HandleFunc("/ws", wsHandler)
	log.Println("WebSocket server started on :8080")
	http.ListenAndServe(":8080", nil)
}

 

⚠️ สำคัญ
เมื่อมี Load Balancer ด้านหน้า → ต้องมี Sticky Session
ไม่เช่นนั้น connection นี้จะไม่ถูกส่งกลับมาที่ Server เดิม

 

📈 การ Scale โดยไม่ทำให้ Connection หลุด

 

❌ สิ่งที่ไม่ควรทำ

  • Kill Pod ทันที
  • Rolling Update แบบไม่สนใจ connection

 

✅ วิธีที่ถูกต้อง

  • Mark Pod เป็น draining
  • ไม่รับ connection ใหม่
  • รอ client disconnect
  • ค่อย terminate Pod

 

🧠 แนวคิดสำคัญ: Stateless WebSocket Server

 

เพื่อให้ Scale ได้จริง

  • อย่าเก็บ state ไว้ใน memory อย่างเดียว
  • ใช้ Redis / External Store
  • Client reconnect แล้ว recover ได้

 

🔁 รองรับ Reconnect อย่างปลอดภัย

 

สิ่งที่ระบบ Production ควรมี

  • Reconnect logic ฝั่ง client
  • Resume session
  • Token-based authentication
  • Idempotent message

 

🧪 สิ่งที่ต้องทดสอบก่อนขึ้น Production

 

  • Scale Pod ระหว่างมี user เชื่อมต่อ
  • Reconnect พร้อมกันจำนวนมาก
  • Chaos Test (สุ่มปิด Pod)
  • ตรวจสอบ message loss และ duplication

 

🚀 ท้าให้ลอง!

 

ลองทำตามนี้

  • เปิด Sticky Session บน Load Balancer
  • Scale WebSocket Server ระหว่างใช้งานจริง
  • สังเกตว่า user หลุดหรือไม่
  • ปรับ timeout และ draining ให้เหมาะสม

 

ถ้าผู้ใช้ไม่รู้สึกอะไรเลย
แปลว่า ระบบคุณผ่านระดับ Production แล้วจริง ๆ ✅

 


 

🔮 EP ถัดไป EP.124 Security & Authentication ขั้นสูงสำหรับ WebSocket

 

ในตอนถัดไป เราจะเจาะลึก

  • JWT & Token Strategy
  • ป้องกัน WebSocket Hijacking
  • Secure Handshake ระดับ Enterprise