คู่มือเกมแลค › L7 OS ของเซิร์ฟเวอร์ (เคอร์เนล)
คิวรอเชื่อมต่อ (backlog) ล้น Listen backlog / SYN queue overflow
ID สาเหตุ so-backlog · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)
เปิดการ์ดในฉบับหลักที่มีภาพและการทดลอง →
ทันทีหลังปิดปรับปรุง ถ้าผู้เล่นหลายหมื่นคนเชื่อมต่อพร้อมกัน คิวรอเชื่อมต่อ (backlog) ของเคอร์เนลจะล้น และความพยายามเชื่อมต่อจะถูกทิ้ง
ทำไม พอปิดปรับปรุงเสร็จ การเชื่อมต่อก็ทะลักเข้ามาเร็วกว่าที่เซิร์ฟเวอร์เกมรับด้วย accept ทัน → ผลคือ คิวรอเชื่อมต่อของเคอร์เนล (backlog คือค่าที่น้อยกว่าระหว่างค่าที่โค้ดเซิร์ฟเวอร์ส่งให้ listen กับเพดานของเคอร์เนล) เต็ม → บนหน้าจอ ความพยายามเชื่อมต่อถูกทิ้งและลองใหม่วนไปเรื่อย ๆ จนเข้าเกมไม่ได้/โหลดไม่จบ
- อาการ
- เข้าเกมไม่ได้/โหลดไม่จบ
- ปัจจัย
- แพ็กเก็ตหาย
- ใครเจอ
- ทั้งเซิร์ฟเวอร์
- เกิดเมื่อไร
- หลังล็อกอิน/หลังปิดปรับปรุง
- ผู้รับผิดชอบ
- ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา), พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)
- งานฝั่งทีมพัฒนาเกม
- เซิร์ฟเวอร์: เพิ่มค่า listen ที่ส่งในโค้ด, อย่าให้เธรดที่รับการเชื่อมต่อหยุดเพราะงานอื่น, ทำระบบคิวล็อกอิน ไคลเอนต์: เพิ่มช่วงห่างการลองใหม่ (สุ่มให้กระจาย)
- งานฝั่งทีมอินฟรา
- เพิ่ม somaxconn ของเคอร์เนล (ต้องเพิ่มคู่กับค่า listen ในโค้ดเซิร์ฟเวอร์จึงจะได้ผล), เปิด SYN cookie ไว้, มอนิเตอร์จำนวนครั้งที่ล้น (TcpExtListenOverflows ของ nstat)
- ตัวเลขที่ควรรู้
- เพดานของเคอร์เนล Linux (somaxconn) มีค่าเริ่มต้น 4,096 ตั้งแต่ 5.4 (ก่อนหน้านั้น 128) แต่ถ้าโค้ดเซิร์ฟเวอร์ส่งค่าที่น้อยกว่าให้ listen ค่านั้นจะกลายเป็นขีดจำกัด เมื่อคิวเต็ม Linux จะทิ้งคำขอเชื่อมต่อเงียบ ๆ โดยไม่ส่งข้อผิดพลาดกลับ OS ฝั่งไคลเอนต์จะส่งซ้ำอีกไม่กี่ครั้งโดยเริ่มหลัง 1 วินาที ผู้เล่นจึงเห็นแค่หน้าโหลดนาน ๆ โดยไม่ขึ้น “เชื่อมต่อไม่สำเร็จ” ส่วนเซิร์ฟเวอร์ Windows จะส่งการตอบกลับว่าปฏิเสธ ไคลเอนต์จึงเห็น “เชื่อมต่อไม่สำเร็จ” ทันที
- บนกราฟ
- พุ่งทันทีหลังเปิดเซิร์ฟ/ปิดปรับปรุง · จำนวนครั้งที่คิวรอเชื่อมต่อล้น (ListenOverflows), จำนวนความพยายามเชื่อมต่อ
- จุดที่ต้องดู
- ค่าที่เพิ่มขึ้นของ TcpExtListenOverflows และ TcpExtListenDrops จาก nstat -az และใช้ ss -ltn เทียบ Recv-Q (จำนวนการเชื่อมต่อที่รอ accept) กับ Send-Q (ขีดจำกัด backlog) ของซ็อกเก็ตที่ listen อยู่
- สัญญาณว่าใช่
- ช่วงที่การเชื่อมต่อแห่เข้ามา ListenOverflows เพิ่มขึ้น และ Recv-Q ของซ็อกเก็ตที่ listen อยู่ชนค่า Send-Q
- สัญญาณว่าไม่ใช่
- ListenOverflows ไม่ขยับ: ไม่ใช่สาเหตุนี้ ถ้าเชื่อมต่อได้แต่โหลดไม่จบ ให้ดู “คนแห่ล็อกอินและคิวรี N+1” ถ้าติดที่จำนวนคนค่าหนึ่งพอดี ให้ดู “ขีดจำกัด file descriptor”
- วิธีตรวจ
- ใช้เครื่องมือฝั่งอินฟรา (ไม่ต้องใช้โค้ดเกม)
แหล่งอ้างอิง
- listen(2) — Linux manual page Linux man-pages
ถ้า backlog ของ listen เกิน somaxconn จะถูกตัดเงียบ ๆ, somaxconn ค่าเริ่มต้น 4,096 (ตั้งแต่ 5.4 ก่อนหน้านั้น 128), เมื่อคิวเต็มอาจเพิกเฉยต่อคำขอแล้วปล่อยให้ไคลเอนต์ลองใหม่เอง - IP Sysctl Linux kernel
tcp_syn_retries: ส่งคำขอเชื่อมต่อ (SYN) ซ้ำหลายครั้ง โดยรอ 1 วินาทีก่อนส่งซ้ำครั้งแรก, tcp_abort_on_overflow ปิดเป็นค่าเริ่มต้น (คิวล้นก็ไม่ส่งการปฏิเสธกลับ), tcp_syncookies เปิดเป็นค่าเริ่มต้น - listen function (winsock2.h) Microsoft
บน Windows เมื่อคิวเต็ม ไคลเอนต์จะได้รับข้อผิดพลาด WSAECONNREFUSED - SNMP counter Linux kernel
TcpExtListenOverflows: จำนวนครั้งที่ทิ้งคำขอเชื่อมต่อ (SYN) เพราะคิว accept เต็ม ซึ่ง TcpExtListenDrops จะเพิ่มขึ้นไปพร้อมกัน - net/ipv4/tcp_diag.c (Linux v6.12) Linux kernel
ค่า Recv-Q และ Send-Q ของ ss: ซ็อกเก็ตที่ listen อยู่คือจำนวนการเชื่อมต่อที่รอ accept และขีดจำกัด backlog, ซ็อกเก็ตที่เชื่อมต่อแล้วคือไบต์ที่แอปยังไม่ได้อ่าน และไบต์ที่ส่งไปแล้วแต่ยังไม่ได้รับ ACK
สาเหตุที่ควรดูประกอบ
ชั้นเดียวกัน: L7 OS ของเซิร์ฟเวอร์ (เคอร์เนล)
สาเหตุจากชั้นอื่นที่ทำให้เกิดอาการเดียวกัน (เข้าเกมไม่ได้/โหลดไม่จบ)
ดูการ์ดในฉบับหลักที่มีภาพและการทดลอง