12/04/2026 18:16น.

EP.108 การจัดการ Timeout และ Connection Cleanup แบบ Advanced
#Production
#WebSocket Server
#Cleanup
#Timeout
#Golang
#Go
#WebSocket
เมื่อ WebSocket Server ของคุณเข้าสู่ Production Environment จริง ปัญหาที่มักพบคือ connection ที่หลุดค้าง (stale) หรือ idle connection ที่ไม่มีการใช้งาน แต่ยังใช้ทรัพยากรของระบบอยู่
หากปล่อยไว้โดยไม่มีการจัดการที่ดี อาจเกิด Resource Leak ซึ่งจะทำให้ CPU, Memory ถูกใช้งานโดยเปล่าประโยชน์ ส่งผลให้ระบบหน่วง ช้า และล่มได้ในที่สุด
ใน EP นี้ เราจะเรียนรู้เทคนิคสำคัญในการ:
- ตั้งเวลา Timeout เพื่อตรวจจับการเชื่อมต่อที่ไม่ตอบสนอง
- ตรวจสอบ connection ที่ idle
- สร้างกลไก Cleanup เพื่อจัดการ stale connection
ทั้งหมดนี้เพื่อให้ WebSocket Server เสถียร, ปลอดภัย และ รองรับการขยายตัวได้ในระดับ Production 🔧
🧩 1. ทำไมต้องมี Timeout และ Cleanup?
WebSocket เป็นการเชื่อมต่อแบบ long-lived ที่อาจค้างอยู่ได้นานโดยไม่ถูกปิดอย่างถูกต้อง เช่น:
- ผู้ใช้ปิด browser ทันทีโดยไม่ส่ง close frame
- การหลุดของเครือข่าย (Network Drop)
- Client ไม่ส่งข้อมูลนานเกินไป
หากไม่มีระบบจัดการที่ดี จะทำให้เกิด:
- ❌ Memory Leak จาก connection ค้าง
- ❌ เพิ่มภาระ CPU โดยไม่จำเป็น
- ❌ จำกัดจำนวนผู้ใช้งานที่ระบบสามารถรองรับได้
🕒 2. เทคนิคการตั้ง Timeout ด้วย Ping/Pong
Go รองรับ Ping/Pong ผ่าน gorilla/websocket ซึ่งใช้ในการตรวจสอบว่า client ยัง active อยู่หรือไม่
✅ ตัวอย่างโค้ด:
const (
pongWait = 10 * time.Second
pingPeriod = (pongWait * 9) / 10
)
conn.SetReadDeadline(time.Now().Add(pongWait))
conn.SetPongHandler(func(string) error {
conn.SetReadDeadline(time.Now().Add(pongWait))
return nil
})
ticker := time.NewTicker(pingPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
log.Println("Ping error:", err)
return
}
default:
_, _, err := conn.ReadMessage()
if err != nil {
log.Println("Connection closed:", err)
return
}
}
}
🔍 คำอธิบาย:
SetReadDeadline: ตั้ง timeout หากไม่ได้รับข้อมูลหรือ pongWriteMessage(PingMessage): ส่ง Ping ไปหา clientSetPongHandler: หาก client ตอบกลับ จะต่ออายุ timeout- หากหมดเวลา → ตัด connection อัตโนมัติ
🧹 3. Cleanup Stale Connections ด้วย Worker
ในระบบขนาดใหญ่ เราอาจมี connection เป็นหมื่น จำเป็นต้องมี worker ที่คอยล้าง connection ที่ไม่ active ออกจาก memory
✅ ตัวอย่างโค้ด:
type Client struct {
ID string
Connection *websocket.Conn
LastActive time.Time
}
func cleanupInactiveConnections() {
for {
time.Sleep(30 * time.Second)
for id, client := range clients {
if time.Since(client.LastActive) > 1*time.Minute {
log.Printf("Closing inactive connection: %s\n", id)
client.Connection.Close()
delete(clients, id)
}
}
}
}
- ตรวจสอบทุก 30 วิ
- หาก inactive เกิน 1 นาที → ปิด connection + ลบออก
⚙️ 4. Best Practices สำหรับ Timeout และ Cleanup
| แนวทาง | รายละเอียด |
|---|---|
| ⏱ กำหนด Read/Write Deadline | ตัด connection ที่ไม่ตอบสนอง |
| 🔄 ใช้ Ping/Pong Mechanism | ตรวจสอบความ active ของ client |
| 🧹 ใช้ Goroutine Cleanup Worker | ลบ connection ที่หมดอายุ |
| 🧠 เก็บ Timestamp ของการใช้งานครั้งล่าสุด | เพื่อใช้ในการตรวจสอบ activity |
| 🧯 จัดการ panic | ป้องกันระบบล่มขณะ cleanup |
🧠 5. Debug และ Monitor การทำงาน
เมื่อติดตั้งระบบ Timeout และ Cleanup แล้ว อย่าลืมใช้:
- Log: เพื่อเก็บข้อมูลเวลาตัด connection
- Metrics: เพื่อตรวจสอบจำนวน active connection
- pprof: ตรวจสอบว่ามี goroutine หรือ memory leak หรือไม่
🚀 ท้าให้ลอง!
ลองเปิด Ping/Pong + Timeout แล้ว:
- ทิ้ง connection ไว้แบบ idle
- ปิด browser โดยไม่ส่ง close
- ดูว่า server ปิด connection ให้อัตโนมัติหรือไม่
คุณจะพบว่า WebSocket Server ของคุณ:
✅ เบาลง
✅ ไม่กิน resource ค้าง
✅ พร้อมรับ load ได้มากขึ้น 🚀
🌟 EP ถัดไป:
EP.109: การปรับปรุง Performance สำหรับ Mobile Devices เรียนรู้เทคนิคทำให้ WebSocket ทำงานบนมือถือได้ดีขึ้น เช่น:
- ลดการใช้พลังงาน
- จัดการการ reconnect เมื่อสัญญาณหาย
- ปรับ timeout สำหรับผู้ใช้ mobile
📱 พร้อมตัวอย่างโค้ดจริงสำหรับระบบ Production!
อ่านบทความ Series อื่นๆ
🔵 Facebook: https://www.facebook.com/superdev.academy.th
🔴 YouTube : Superdev Academy
📸 Instagram: Superdev Academy
🎬 TikTok: https://www.tiktok.com/@superdevacademy?lang=th-TH
🌐 Website: https://www.superdevacademy.com/