12/04/2026 18:16น.

Golang The Series EP.138: Monitoring Latency & Performance – รีดความเร็ว WebSocket ให้ลื่นระดับเทพ
#Superdev Academy
#Performance Optimization
#Latency
#WebSocket
#Golang
รีดความเร็วระดับมิลลิวินาที ให้ระบบลื่นไหลไม่มีสะดุด
ยินดีต้อนรับชาว Gopher ทุกท่านครับ! ในโลกของระบบ Real-time คำถามที่ว่า "Server รับคนได้กี่คน?" นั้นเป็นเพียงแค่ปริมาณ แต่สิ่งที่ตัดสินว่าระบบของคุณ "เจ๋ง" จริงหรือไม่คือ "Latency" หรือความหน่วงครับ
ลองจินตนาการว่าคุณกำลังเล่นแอปเทรดหุ้น หรือแอปแชทที่ส่งข้อความไปแล้วต้องรอ 1-2 วินาทีกว่าอีกฝ่ายจะเห็น นั่นไม่ใช่ประสบการณ์ WebSocket ที่ดีครับ วันนี้เราจะมาสวมวิญญาณนักปรับแต่ง (Tuner) เพื่อมองหา "ความช้าที่มองไม่เห็น" และกำจัดมันให้สิ้นซากครับ
1. การวัด Latency: เลิกเชื่อค่า Average (เฉลี่ย) ได้แล้ว!
หนึ่งในความผิดพลาดที่ร้ายแรงที่สุดของ Developer คือการดูแค่ Average Latency เพราะค่าเฉลี่ยจะกลบปัญหาที่เกิดขึ้นกับผู้ใช้บางกลุ่มไปจนหมด สิ่งที่เราต้องโฟกัสคือ Percentiles ($P_{95}, P_{99}$):
- Average ($P_{50}$): ค่ากลางๆ ที่คนส่วนใหญ่เจอ
- $P_{95}$: 95% ของผู้ใช้ได้รับข้อมูลภายในเวลานี้ (เริ่มเห็นปัญหา)
- $P_{99}$: 1% ของผู้ใช้ที่โชคร้ายที่สุดเจอความช้าเท่าไหร่? (นี่คือ Tail Latency ที่มักเกิดจาก GC หรือ Network Congestion)
Superdev Tip: หาก $P_{50}$ ของคุณอยู่ที่ 20ms แต่ $P_{99}$ พุ่งไปที่ 2,000ms แสดงว่าระบบคุณมีอาการ "สะอึก" (Spike) ซึ่งมักเกิดจาก Stop-the-world ของ Garbage Collector หรือการติด Lock (Mutex) นานเกินไปครับ
2. เจาะลึกจุดที่ทำให้เกิดความล่าช้า (The Bottlenecks)
Latency ใน WebSocket ไม่ได้มาจากโค้ด Go อย่างเดียว แต่มันคือผลรวมของ 3 ปัจจัย:
- Network Latency: ระยะทางทางกายภาพ (Speed of Light) แก้ได้ด้วยการขยับ Server ไปใกล้ User (Edge Computing/AWS Global Accelerator)
- OS/Stack Latency: การจัดการ Context Switching ของ OS และ TCP Buffer ที่ไม่เหมาะสม
- Application Latency:
- Lock Contention: การใช้ sync.Mutex ในจุดที่มี Traffic หนาแน่นจน Goroutines ต้องเข้าแถวรอ
- Garbage Collection (GC): การจอง Memory พร่ำเพรื่อจน Go ต้องหยุดทำงานเพื่อกวาดขยะ
3. กลยุทธ์การ Optimization ฉบับ Go Expert
A. ลดการจองหน่วยความจำด้วย sync.Pool
การสร้าง Object (เช่น Buffer สำหรับอ่านข้อความ) ใหม่ทุกครั้งที่ส่งข้อความคือ "ภาระ" ของระบบครับ เราควรนำของเก่ามาใช้ใหม่ (Reuse)
Go
var bufferPool = sync.Pool{
New: func() any {
// จอง Buffer ขนาด 4KB เตรียมไว้
return make([]byte, 4096)
},
}
func handleMessage(conn *websocket.Conn) {
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf) // คืนเข้า Pool เมื่อใช้เสร็จ
_, message, _ := conn.ReadMessage()
copy(buf, message)
// ประมวลผลต่อโดยไม่สร้างภาระให้ GC
}
B. ปรับแต่ง TCP Stack (TCP_NODELAY)
โดยปกติ TCP จะใช้ Nagle's Algorithm เพื่อรอรวมข้อมูลชิ้นเล็กๆ เป็นก้อนใหญ่ก่อนส่ง (เพื่อประหยัด Bandwidth) แต่สำหรับ WebSocket มันคือ "ยาพิษ" เพราะทำให้ข้อมูลดีเลย์
- Solution: ตรวจสอบว่า Library ของคุณเปิด SetNoDelay(true) หรือยัง (ใน Go net package จะเปิดให้เป็น Default อยู่แล้ว แต่ถ้าผ่าน Proxy/Load Balancer ต้องเช็คจุดนั้นด้วยครับ)
C. ย้ายจาก JSON ไปเป็น Binary (Protobuf)
JSON กิน CPU ในการ Serialize/Deserialize สูงมาก และมีขนาดใหญ่ (Verbose) การเปลี่ยนมาใช้ Protocol Buffers (Protobuf) จะช่วยลดขนาด Packet ลงได้ 30-70% ซึ่งหมายถึง Latency ที่ลดลงในระดับ Network ด้วยครับ
4. Monitoring แบบ End-to-End ด้วย Tracing
ถ้า Server บอกว่าประมวลผลเสร็จใน 5ms แต่ User บ่นว่าช้า? ปัญหาระหว่างทางคือสิ่งที่คุณต้องพิสูจน์ครับ
เราใช้ OpenTelemetry (OTel) ในการฉีด Trace ID เข้าไปใน WebSocket Handshake:
- คุณจะเห็นทันทีว่าข้อมูลค้างอยู่ที่ Load Balancer นานเท่าไหร่
- ค้างอยู่ที่ Redis (Pub/Sub) นานเท่าไหร่
- และออกไปถึงหน้าจอ User เมื่อไหร่
การเห็นภาพแบบ Timeline จะทำให้คุณเลิก "เดา" แล้วเริ่ม "แก้" ได้ถูกจุดครับ
5. การจูน Write Buffer และ Flush Strategy
ในระบบที่มีการส่งข้อความถี่มาก (เช่น กราฟหุ้น) การส่งออกไปทีละข้อความ (Write) จะทำให้เกิด System Call บ่อยจน CPU Peak
- Tuning: ใช้การทำ Write Coalescing (รวมข้อความสั้นๆ ในช่วงเวลาไม่กี่มิลลิวินาทีแล้วส่งออกไปทีเดียว) เพื่อลดจำนวน System Call โดยไม่กระทบต่อความรู้สึก Real-time ของผู้ใช้
สรุป
Monitoring & Performance Optimization ไม่ใช่การตั้งค่าครั้งเดียวแล้วจบ แต่เป็นงานฝีมือที่ต้องอาศัยข้อมูล (Data-driven) การทำให้ระบบเร็วขึ้น 100ms อาจดูเหมือนน้อยในเชิงตัวเลข แต่มันคือความต่างระหว่าง "แอปที่ให้ความรู้สึกติดขัด" กับ "แอปที่ลื่นไหลระดับเทพ" ครับ
ในตอนหน้า (EP.139): เราจะขยับไปดูเคสที่ท้าทายที่สุดคือ Best Practices สำหรับ Mobile & Low-bandwidth Environment — เมื่อเน็ตมือถือของ User ไม่เป็นใจ (3G/4G หรือเน็ตแกว่ง) เราจะทำอย่างไรให้ WebSocket ยังทำงานได้เสถียร! ห้ามพลาดครับ