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

Sách trắng lag game › L7 OS server (kernel)

Bộ đệm socket của kernel quá nhỏ Small socket buffers

ID nguyên nhân so-sockbuf · Phụ trách chính Hạ tầng server (Đội hạ tầng) · Phối hợp Phát triển server (Đội phát triển game)

Mở thẻ gốc có hình minh họa và thí nghiệm →

Khi bộ đệm gửi và nhận nhỏ, lúc lưu lượng dồn thành burst, gói UDP nhận về sẽ bị bỏ, còn việc gửi qua TCP bị chặn vì bộ đệm hết chỗ.

Vì sao SO_SNDBUF và SO_RCVBUF để mặc định hoặc quá nhỏ → Dẫn đến Khi có burst hoặc khi thread nhận dừng một chút, bộ đệm nhận UDP bị tràn và gói bị bỏ; TCP phải chờ vì bộ đệm gửi hết chỗ → Trên màn hình Dịch chuyển tức thời (mất gói UDP) hoặc tua nhanh (TCP phải chờ)

Triệu chứng
Dịch chuyển tức thời, Tua nhanh
Yếu tố
Mất gói, Ngưng trệ
Ai gặp phải
Cả server
Khi nào
Khi đông người
Phụ trách
Phụ trách chính Hạ tầng server (Đội hạ tầng) · Phối hợp Phát triển server (Đội phát triển game)
Việc cần làm (Đội phát triển game)
Đặt kích thước bộ đệm (SO_SNDBUF, SO_RCVBUF) trong code cho phù hợp với lưu lượng; lưu ý với TCP, tự đặt kích thước sẽ tắt cơ chế tự điều chỉnh kích thước của Linux; không đặt quá lớn vì dữ liệu cũ sẽ dồn trong bộ đệm và làm tăng độ trễ; không để thread nhận bị dừng.
Việc cần làm (Đội hạ tầng)
Điều chỉnh giới hạn của kernel (rmem_max, wmem_max; kích thước đặt trong code cũng không vượt được giá trị này) và giá trị mặc định (rmem_default), giám sát bộ đếm tràn bộ đệm (RcvbufErrors).
Con số tham khảo
Bộ đệm nhận UDP mặc định trên Linux khoảng 208 KB. Ngay cả gói nhỏ cũng chiếm bộ nhớ kernel nhiều hơn kích thước thật rất nhiều, nên chỉ vài chục đến vài trăm gói là đầy. Với server nhận 100.000 gói mỗi giây, thread nhận chỉ cần dừng vài ms là bộ đệm tràn.
Trên đồ thị
Thỉnh thoảng vọt lên bất chợt · Số lần tràn bộ đệm nhận UDP (UdpRcvbufErrors)
Chỗ cần xem
Mức tăng UdpRcvbufErrors trong nstat -az và skmem trong ss -uamn (rb là kích thước bộ đệm nhận, d là số gói bị bỏ vì không đưa được vào socket); với TCP, xem trong skmem của ss -tm bộ nhớ chờ gửi (w) đã chạm kích thước bộ đệm gửi (tb) chưa
Đúng nếu
UdpRcvbufErrors (hoặc d của socket) tăng vào lúc có burst hoặc thread nhận bị dừng, và rb ở gần giá trị mặc định (khoảng 208 KB). Với TCP, w dính sát tb và send bị chặn
Loại trừ nếu
bộ đếm không đổi mà vẫn mất gói → vấn đề ở tầng NIC (“Ring buffer không đủ”) hoặc trên đường mạng
Cách kiểm tra
Kiểm tra bằng công cụ hạ tầng (không cần code game)

Nguồn

  1. socket(7) — Linux manual page Linux man-pages
    Giá trị mặc định của SO_RCVBUF và SO_SNDBUF là rmem_default và wmem_default, giới hạn trên là rmem_max và wmem_max; kernel nhân đôi giá trị được đặt
  2. include/net/sock.h (Linux v6.18) Linux kernel
    Bộ đệm socket mặc định được định nghĩa bằng dung lượng của 256 gói 256 byte tính cả overhead của sk_buff (SKB_TRUESIZE(256)×256); frame nhỏ cũng bị tính bằng sk_buff+MTU (khoảng 208 KB là giá trị tính trên x86-64)
  3. IP Sysctl Linux kernel
    tcp_rmem, tcp_wmem: tự đặt SO_RCVBUF hoặc SO_SNDBUF sẽ tắt cơ chế tự điều chỉnh kích thước của socket đó
  4. net/ipv4/udp.c (Linux v6.12) Linux kernel
    Khi hàng đợi nhận UDP vượt kích thước bộ đệm socket, gói bị bỏ ngay và RcvbufErrors tăng
  5. net/ipv4/proc.c (Linux v6.12) Linux kernel
    Tên bộ đếm mà nstat hiển thị: RcvbufErrors và SndbufErrors trong nhóm Udp
  6. ss(8) — Linux manual page iproute2
    skmem của -m: rb là kích thước bộ đệm nhận, tb là kích thước bộ đệm gửi, w là bộ nhớ chờ gửi, d là số gói bị bỏ trước khi vào socket

Nguyên nhân nên xem cùng

Cùng tầng: L7 OS server (kernel)

Nguyên nhân ở tầng khác gây cùng triệu chứng (Dịch chuyển tức thời)

Xem thẻ gốc có hình minh họa và thí nghiệm