คู่มือเกมแลค › L7 OS ของเซิร์ฟเวอร์ (เคอร์เนล)
บัฟเฟอร์ซ็อกเก็ตของเคอร์เนลไม่พอ Small socket buffers
ID สาเหตุ so-sockbuf · ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)
เปิดการ์ดในฉบับหลักที่มีภาพและการทดลอง →
ถ้าบัฟเฟอร์ส่งและรับเล็ก เมื่อทราฟฟิกมาเป็น burst แพ็กเก็ตที่รับทาง UDP จะถูกทิ้ง ส่วนการส่งทาง TCP จะติดเพราะบัฟเฟอร์ไม่มีที่ว่าง
ทำไม SO_SNDBUF และ SO_RCVBUF ใช้ค่าเริ่มต้นหรือเล็กเกินไป → ผลคือ ระหว่าง burst หรือช่วงที่เธรดรับหยุดชั่วครู่ receive buffer ของ UDP ล้นจนแพ็กเก็ตถูกทิ้ง ส่วน TCP ต้องรอเพราะ send buffer ไม่มีที่ว่าง → บนหน้าจอ วาร์ป (UDP แพ็กเก็ตหาย) หรือกรอเร็ว (TCP รอ)
- อาการ
- วาร์ป, กรอเร็ว
- ปัจจัย
- แพ็กเก็ตหาย, การหยุดชะงัก
- ใครเจอ
- ทั้งเซิร์ฟเวอร์
- เกิดเมื่อไร
- ตอนคนแห่มารวมกัน
- ผู้รับผิดชอบ
- ผู้รับผิดชอบหลัก อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา) · ร่วมกับ พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม)
- งานฝั่งทีมพัฒนาเกม
- ตั้งขนาดบัฟเฟอร์ (SO_SNDBUF, SO_RCVBUF) ในโค้ดให้เหมาะกับทราฟฟิก, ระวังว่าถ้ากำหนดขนาดบัฟเฟอร์ของ TCP เอง Linux จะปิดการปรับขนาดอัตโนมัติ, อย่าตั้งใหญ่เกินไปเพราะข้อมูลเก่าจะกองในบัฟเฟอร์จนดีเลย์เพิ่ม, อย่าให้เธรดรับหยุด
- งานฝั่งทีมอินฟรา
- ปรับเพดานของเคอร์เนล (rmem_max, wmem_max: ขนาดบัฟเฟอร์ที่ตั้งในโค้ดก็เกินค่านี้ไม่ได้) และค่าเริ่มต้น (rmem_default), มอนิเตอร์ตัวนับบัฟเฟอร์ล้น (RcvbufErrors)
- ตัวเลขที่ควรรู้
- ค่าเริ่มต้นของ receive buffer ของ UDP บน Linux อยู่ที่ประมาณ 208 KB แพ็กเก็ตเล็ก ๆ หนึ่งแพ็กเก็ตก็กินหน่วยความจำเคอร์เนลมากกว่าขนาดจริงมาก แค่หลายสิบถึงหลายร้อยแพ็กเก็ตก็เต็ม ถ้าเซิร์ฟเวอร์รับ 100,000 แพ็กเก็ตต่อวินาที เธรดรับหยุดแค่ไม่กี่ ms บัฟเฟอร์ก็ล้น
- บนกราฟ
- พุ่งแบบสุ่มเป็นครั้งคราว · receive buffer ของ UDP ล้น (UdpRcvbufErrors)
- จุดที่ต้องดู
- ค่าที่เพิ่มขึ้นของ UdpRcvbufErrors จาก nstat -az และ skmem ใน ss -uamn (rb คือขนาด receive buffer, d คือจำนวนแพ็กเก็ตที่ใส่ซ็อกเก็ตไม่ได้จนถูกทิ้ง) ส่วน TCP ดู skmem ของ ss -tm ว่าหน่วยความจำที่รอส่ง (w) ชนขนาด send buffer (tb) หรือไม่
- สัญญาณว่าใช่
- ช่วง burst หรือตอนที่เธรดรับหยุด UdpRcvbufErrors (หรือ d ของซ็อกเก็ต) เพิ่มขึ้น และ rb อยู่ใกล้ค่าเริ่มต้น (ประมาณ 208 KB) ส่วน TCP: w ติดอยู่ที่ tb และ send ถูกบล็อก
- สัญญาณว่าไม่ใช่
- ตัวนับไม่ขยับแต่มีแพ็กเก็ตหาย: น่าจะเป็นที่ขั้น NIC (“ring buffer ไม่พอ”) หรือช่วงเครือข่าย
- วิธีตรวจ
- ใช้เครื่องมือฝั่งอินฟรา (ไม่ต้องใช้โค้ดเกม)
แหล่งอ้างอิง
- socket(7) — Linux manual page Linux man-pages
ค่าเริ่มต้นของ SO_RCVBUF และ SO_SNDBUF คือ rmem_default และ wmem_default, เพดานคือ rmem_max และ wmem_max, เคอร์เนลจะเพิ่มค่าที่ตั้งเป็นสองเท่า - include/net/sock.h (Linux v6.18) Linux kernel
กำหนดบัฟเฟอร์ซ็อกเก็ตเริ่มต้นเท่ากับแพ็กเก็ตขนาด 256 ไบต์จำนวน 256 แพ็กเก็ตรวม overhead ของ sk_buff (SKB_TRUESIZE(256)×256), เฟรมเล็ก ๆ ก็คิดเป็น sk_buff+MTU (ประมาณ 208 KB คือค่าที่คำนวณบน x86-64) - IP Sysctl Linux kernel
tcp_rmem, tcp_wmem: ถ้ากำหนด SO_RCVBUF หรือ SO_SNDBUF เอง การปรับขนาดอัตโนมัติของซ็อกเก็ตนั้นจะปิด - net/ipv4/udp.c (Linux v6.12) Linux kernel
ถ้าคิวรับของ UDP เกินขนาดบัฟเฟอร์ซ็อกเก็ต จะทิ้งทันทีและเพิ่มค่า RcvbufErrors - net/ipv4/proc.c (Linux v6.12) Linux kernel
ชื่อตัวนับที่ nstat แสดง: RcvbufErrors และ SndbufErrors ในกลุ่ม Udp - ss(8) — Linux manual page iproute2
skmem ของ -m: rb ขนาด receive buffer, tb ขนาด send buffer, w หน่วยความจำที่รอส่ง, d จำนวนแพ็กเก็ตที่ถูกทิ้งก่อนเข้าซ็อกเก็ต
สาเหตุที่ควรดูประกอบ
ชั้นเดียวกัน: L7 OS ของเซิร์ฟเวอร์ (เคอร์เนล)
สาเหตุจากชั้นอื่นที่ทำให้เกิดอาการเดียวกัน (วาร์ป)
ดูการ์ดในฉบับหลักที่มีภาพและการทดลอง