한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

คู่มือเกมแลค › 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 ไม่พอ”) หรือช่วงเครือข่าย
วิธีตรวจ
ใช้เครื่องมือฝั่งอินฟรา (ไม่ต้องใช้โค้ดเกม)

แหล่งอ้างอิง

  1. socket(7) — Linux manual page Linux man-pages
    ค่าเริ่มต้นของ SO_RCVBUF และ SO_SNDBUF คือ rmem_default และ wmem_default, เพดานคือ rmem_max และ wmem_max, เคอร์เนลจะเพิ่มค่าที่ตั้งเป็นสองเท่า
  2. 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)
  3. IP Sysctl Linux kernel
    tcp_rmem, tcp_wmem: ถ้ากำหนด SO_RCVBUF หรือ SO_SNDBUF เอง การปรับขนาดอัตโนมัติของซ็อกเก็ตนั้นจะปิด
  4. net/ipv4/udp.c (Linux v6.12) Linux kernel
    ถ้าคิวรับของ UDP เกินขนาดบัฟเฟอร์ซ็อกเก็ต จะทิ้งทันทีและเพิ่มค่า RcvbufErrors
  5. net/ipv4/proc.c (Linux v6.12) Linux kernel
    ชื่อตัวนับที่ nstat แสดง: RcvbufErrors และ SndbufErrors ในกลุ่ม Udp
  6. ss(8) — Linux manual page iproute2
    skmem ของ -m: rb ขนาด receive buffer, tb ขนาด send buffer, w หน่วยความจำที่รอส่ง, d จำนวนแพ็กเก็ตที่ถูกทิ้งก่อนเข้าซ็อกเก็ต

สาเหตุที่ควรดูประกอบ

ชั้นเดียวกัน: L7 OS ของเซิร์ฟเวอร์ (เคอร์เนล)

สาเหตุจากชั้นอื่นที่ทำให้เกิดอาการเดียวกัน (วาร์ป)

ดูการ์ดในฉบับหลักที่มีภาพและการทดลอง