การดู : 0

12/04/2026 18:16น.

Golang The Series EP 132: Cloud Cost Optimization สำหรับ WebSocket

Golang The Series EP 132: Cloud Cost Optimization สำหรับ WebSocket

#WebSocket

#Optimization

#Cloud Cost

#Golang

ยินดีต้อนรับชาว Gopher ทุกท่านครับ! ในโลกของการทำระบบ Real-time การทำให้ระบบ "รันได้" นั้นเป็นเพียงก้าวแรก แต่การทำให้ระบบ "รันได้อย่างยั่งยืนและคุ้มค่า" (Cost-effective) คือหัวใจสำคัญที่จะช่วยให้โปรเจกต์ของคุณเติบโตได้โดยไม่กินทุนตัวเอง

 

WebSocket เป็นเหมือน "สัตว์ประหลาด" ที่กินทรัพยากรแบบ Stateful ยิ่งมีผู้ใช้มาก เครื่องยิ่งต้องใช้ RAM มหาศาล และ Load Balancer ต้องทำงานหนักตลอด 24 ชั่วโมง วันนี้เราจะมาดูเทคนิคการรีดประสิทธิภาพเพื่อบีบค่าใช้จ่ายให้เหลือต่ำที่สุดกันครับ

 

1. ศึก Load Balancer: L4 (NLB) vs L7 (ALB)

 

จุดเริ่มต้นของค่าใช้จ่ายที่หลายคนมองข้ามคือตัว Load Balancer ครับ:

 

คุณสมบัติ

Application Load Balancer (L7)

Network Load Balancer (L4)

ความฉลาด

สูง (รู้จัก Path, Header, Cookie)

ต่ำ (รู้จักแค่ IP และ Port)

การคิดราคา

คิดตามจำนวน Connection และ LCU (แพงกว่า)

คิดตามปริมาณข้อมูล (ถูกกว่ามาก)

ความเหมาะสม

ระบบที่ต้องการ Complex Routing

ระบบ WebSocket ที่มี Connection จำนวนมาก

 

Optimization Tip: สำหรับระบบ WebSocket ขนาดใหญ่ แนะนำให้ใช้ NLB แล้วจัดการเรื่อง SSL Termination ที่ตัวแอป Go เอง หรือใช้ Sidecar อย่าง Nginx/Envoy จะช่วยประหยัดค่าเช่า Load Balancer ไปได้มหาศาลครับ

 

ตัวอย่างการทำ SSL Termination ใน Go (สำหรับใช้คู่กับ NLB):

 

Go
// การรัน Server แบบ HTTPS โดยตรงใน Go เพื่อเซฟค่า L7 Load Balancer
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/ws", serveWebSocket)

    server := &http.Server{
        Addr:    ":443",
        Handler: mux,
        // การตั้งค่า TLS Config เพื่อความปลอดภัยระดับสูงสุด
        TLSConfig: &tls.Config{
            MinVersion: tls.VersionTLS12,
        },
    }

    log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}

 

2. จัดการ "Idle Connections" (คนเปิดทิ้งไว้แต่ไม่คุย)

 

ในระบบ Real-time จะมีผู้ใช้จำนวนมากที่เปิดแอปทิ้งไว้แต่ไม่ได้ส่งข้อมูล (Idle) ท่อที่เปิดค้างไว้เหล่านี้ยังคงกิน RAM ของ Server เราอยู่ตลอดเวลา

 

เทคนิคการประหยัด:

  • Aggressive Timeout: ตั้งค่า SetReadDeadline ให้เหมาะสม หากไม่มีกิจกรรมนานเกินไป ให้ปิดการเชื่อมต่อเพื่อให้ทรัพยากรว่างสำหรับคนอื่น

 

Go
// การตั้งค่า Deadline เพื่อตัดพวก Idle Connections
func (c *Client) readPump() {
    // ตั้งค่าให้ต้องมีการส่งข้อมูลเข้ามาภายใน 15 นาที ไม่เช่นนั้นจะตัดการเชื่อมต่อ
    c.conn.SetReadLimit(maxMessageSize)
    c.conn.SetReadDeadline(time.Now().Add(15 * time.Minute))
    
    c.conn.SetPongHandler(func(string) error { 
        // ถ้าได้รับ Pong ให้ยืดเวลาออกไปอีก 15 นาที
        c.conn.SetReadDeadline(time.Now().Add(15 * time.Minute))
        return nil 
    })
    // ... rest of the code
}

 

3. ลด Memory Footprint ต่อหนึ่งการเชื่อมต่อ

 

ภาษา Go ขึ้นชื่อเรื่องการใช้ Memory น้อย แต่เมื่อต้องรับมือกับ "ล้านการเชื่อมต่อ" (C10M) ทุก KB มีค่าครับ:

  • Buffer Pooling (sync.Pool): ใช้เพื่อนำ Buffer ที่ใช้แล้วกลับมาใช้ใหม่ ลดภาระของ Garbage Collector (GC) ไม่ให้ทำงานหนักเกินไปจน CPU Peak

 

Go
// การใช้ sync.Pool เพื่อลดการจอง Memory ใหม่ทุกครั้งที่มีการอ่าน Message
var readBufferPool = sync.Pool{
    New: func() any {
        return make([]byte, 4096) // จองไว้เครื่องละ 4KB
    },
}

func (c *Client) handleMessage() {
    buf := readBufferPool.Get().([]byte)
    defer readBufferPool.Put(buf) // คืน Buffer เข้า Pool เมื่อใช้เสร็จ

    _, message, _ := c.conn.ReadMessage()
    copy(buf, message)
    // ประมวลผลข้อมูลใน buf...
}

 

4. Auto-scaling ตาม "Connection Count" ไม่ใช่ CPU

 

นี่คือจุดผิดพลาดที่พบบ่อยที่สุด! WebSocket Server มักจะมี CPU Usage ที่ต่ำมาก แต่ RAM จะเต็มก่อนเนื่องจากจำนวน Connection

 

กลยุทธ์การ Scale ที่ถูกต้อง:

  • Custom Metrics: ใช้ Prometheus ส่งค่าจำนวน Active Connections ไปยัง Cloud Provider เพื่อใช้ในการสั่ง Scale

 

Go
var (
    // สร้าง Gauge เพื่อวัดจำนวนคนออนไลน์แบบ Real-time
    activeConnections = promauto.NewGauge(prometheus.GaugeOpts{
        Name: "websocket_active_connections",
        Help: "จำนวน Client ที่เชื่อมต่ออยู่ ณ ปัจจุบัน",
    })
)

func serveWebSocket(w http.ResponseWriter, r *http.Request) {
    activeConnections.Inc() // เพิ่มจำนวนคนออนไลน์
    defer activeConnections.Dec() // ลดจำนวนเมื่อหลุดการเชื่อมต่อ
    
    // ... logic การเชื่อมต่อ WebSocket
}

 

5. พลังของ Spot Instances (ประหยัดได้ถึง 90%)

 

หากคุณออกแบบระบบให้มี High Availability และมีตัวกลางอย่าง Redis Pub/Sub (จาก EP 129 & 130) คุณจะสามารถใช้ Spot Instances ได้อย่างมั่นใจ

 

Spot Instances คือเครื่องที่ Cloud Provider เอามาเลหลังในราคาถูกกว่าปกติ 70-90% โดยมีข้อแม้ว่าเครื่องอาจถูกดึงคืนเมื่อไหร่ก็ได้ แต่ด้วยสถาปัตยกรรมที่เราวางไว้ เมื่อเครื่องหนึ่งหายไป Client จะ Reconnect ไปยังเครื่องอื่นทันที ทำให้เราได้ระบบที่อึดถึกทนในราคาสุดประหยัดครับ

 


 

สรุป

 

การทำ Cloud Cost Optimization ไม่ใช่การ "ขี้เหนียว" แต่คือการใช้ทรัพยากรให้เกิดประโยชน์สูงสุด (Efficiency) การเข้าใจความต่างของ Load Balancer, การบริหารจัดการ Memory ใน Go และการใช้กลยุทธ์การสเกลที่เหมาะสม จะช่วยให้โปรเจกต์ของคุณเติบโตได้อย่างมั่นคงและมีกำไรครับ

 

ในตอนหน้า (EP 133): เราจะยกระดับจากการส่งข้อความธรรมดา ไปสู่เรื่องที่ท้าทายกว่าอย่าง Real-time Analytics & Metrics Streaming วิธีการจัดการข้อมูลมหาศาลให้ไหลจากระบบไปแสดงผลเป็นกราฟแบบวินาทีต่อวินาที! ห้ามพลาดครับ