ฉันมีความล่าช้าในการส่งข้อมูลผ่านช่องทาง TCP ที่ฉันไม่เข้าใจ ลิงก์นี้เป็นลิงก์ 1Gb ที่มีเวลาแฝงแบบ end-to-end ประมาณ 40ms ในการตั้งค่าปัจจุบันของฉัน เวลาแฝง (เวลาจากข้อความหนึ่งที่จะเปลี่ยนจากพื้นที่ผู้ใช้ผู้ส่งไปยังพื้นที่ผู้ใช้ผู้รับ) สามารถเข้าถึง 100ms

ซ็อกเก็ตผู้ส่งได้รับการกำหนดค่าด้วยตัวเลือก TCP_NODELAY บัฟเฟอร์ผู้ส่ง (SO_SNDBUF) ถูกกำหนดค่าให้เป็น 8MB บัฟเฟอร์การรับ (SO_RCVBUF) ได้รับการกำหนดค่าให้เป็น 8MB ด้วย เปิดใช้งานการปรับขนาดหน้าต่าง Tcp

update-1 : ฉันใช้มิดเดิลแวร์ zeromq 3.1.1 เพื่อส่งข้อมูล การกำหนดค่าซ็อกเก็ต รวมถึงการตั้งค่าสถานะ TCP_NODELAY ดำเนินการโดยมิดเดิลแวร์ บางตัวเลือกสามารถเข้าถึงได้เช่น rx และ tx emit ขนาดบัฟเฟอร์ แต่ไม่ใช่ TCP_NODELAY เท่าที่ฉันเข้าใจ TCP_NODELAY ถูกเปิดใช้งานเพื่อให้แน่ใจว่าข้อมูลถูกส่งออกไปมากที่สุด ในระหว่างนี้ การส่งซ็อกเก็ตจริงและการตัดสินใจที่จะส่งข้อความจะดำเนินการในสองเธรดแยกกัน การจัดชุดงานที่เหมาะสมจะเสร็จสิ้นหากมีข้อความหลายข้อความในขณะที่ส่งข้อความแรกในชุดงาน

ฉันทำการจับภาพด้วย tcpdump ซึ่งได้แยกเฟรมด้านล่างออกมา หลังจากการจับมือ TCP เริ่มต้น ผู้ส่ง (172.17.152.124) จะเริ่มส่งข้อมูล ขนาดหน้าต่างเริ่มต้นคือ 5840 ไบต์สำหรับตัวรับ & 5792 ไบต์สำหรับผู้ส่ง

ปัญหาของฉันคือผู้ส่งส่งสองเฟรม (#6 และ #7) แล้วหยุดรอ ack กลับมาจากผู้รับ เท่าที่ฉันเห็น ขนาดหน้าต่างของตัวรับไม่ถึง และการถ่ายโอนไม่ควรหยุด (384 ไบต์คงเหลือด้วยขนาดหน้าต่างรับเริ่มต้น 5840 ไบต์) ฉันเริ่มคิดว่าฉันไม่เข้าใจอย่างถูกต้องว่า TCP คืออะไร ใครก็ได้ช่วยชี้แจงที

update-2 : เพย์โหลดข้อมูลของฉันประกอบด้วยตัวเลขมหัศจรรย์ตามด้วยประทับเวลา ฉันได้แยกแพ็กเก็ตที่ล่าช้าโดยการเปรียบเทียบการประทับเวลาของเพย์โหลดกับการประทับเวลาของ tcpdump payload ts ของเฟรม #9 อยู่ใกล้กับเฟรม #6 และ #7 มากและน้อยกว่าการประทับเวลาของ ack ที่ได้รับในเฟรม #8 อย่างชัดเจน

update-1 : ความจริงที่ว่าเฟรม #9 ไม่ได้ส่งในทันทีสามารถอธิบายได้โดยการเริ่มต้นช้าของช่อง TCP อันที่จริง ปัญหายังปรากฏขึ้นเมื่อการเชื่อมต่อทำงานเป็นเวลาหลายนาที ดังนั้นการเริ่มช้าดูเหมือนจะไม่ใช่คำอธิบายทั่วไป

  1. 20:53:26.017415 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [S], seq 2473022771, win 5840, options [mss 1460,sackOK,TS val 4219180820 ecr 0,nop,wscale 8], length 0

  2. 20:53:26.017423 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [S.], seq 2948065596, ack 2473022772, win 5792, options [mss 1460,sackOK,TS val 186598852 ecr 219180820,nop,wscale 9], length 0

  3. 20:53:26.091940 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [.], ack 1, win 23, options [nop,nop,TS val 4219180894 ecr 186598852], length 0

  4. 20:53:26.091958 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [P.], seq 1:15, ack 1, w in 23, options [nop,nop,TS val 4219180895 ecr 186598852], length 14

  5. 20:53:26.091964 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [.], ack 15, win 12, options [nop,nop,TS val 186598927 ecr 4219180895], length 0

  6. 20:53:26.128298 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.], seq 1:257, ack 15, win 12, options [nop,nop,TS val 186598963 ecr 4219180895], length 256

  7. 20:53:26.128519 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.], seq 257:385, ack 15, win 12, options [nop,nop,TS val 186598963 ecr 4219180895], length 128

  8. 20:53:26.202465 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [.], ack 257, win 27, options [nop,nop,TS val 4219181005 ecr 186598963], length 0

  9. 20:53:26.202475 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [.], seq 385:1833, ack 15, win 12, options [nop,nop,TS val 186599037 ecr 4219181005], length 1448

  10. 20:53:26.202480 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.], seq 1833:2305, ack 15, win 12, options [nop,nop,TS val 186599037 ecr 4219181005], length 472

หากเป็นกรณีนี้ ปลายทั้งสองข้างจะเป็นกล่อง Linux RHEL5 โดยมีเคอร์เนล 2.6.18 และการ์ดเครือข่ายใช้ไดรเวอร์ e1000e

update-3 เนื้อหาของ /etc/sysctl.conf

[[email protected] ~]$ cat /etc/sysctl.conf | grep -v "^#" | grep -v "^$" 
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.ipv4.tcp_rmem = 65536 4194304 16777216
net.ipv4.tcp_wmem = 65536 4194304 16777216 
net.core.netdev_max_backlog = 10000 
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_mem = 262144 4194304 16777216
kernel.shmmax = 68719476736
answer

หลังจากสำรวจการรับส่งข้อมูลของฉันเพิ่มขึ้นอีกเล็กน้อย ฉันก็สามารถเห็นได้ว่าข้อมูลของฉันไม่ได้เป็นอะไรนอกจากลำดับของการระเบิดเล็กๆ น้อยๆ ที่มีช่วงเวลาว่างเล็กน้อยระหว่างพวกเขา

ด้วยเครื่องมือที่มีประโยชน์ssฉันสามารถดึงขนาดหน้าต่างความแออัดปัจจุบันของการเชื่อมต่อของฉัน (ดูcwndค่าในผลลัพธ์):

[[email protected] ~]$ /usr/sbin/ss -i -t -e | grep -A 1 56001

ESTAB 0 0 192.168.1.1:56001
192.168.2.1:45614 uid:1001 ino:6873875 sk:17cd4200ffff8804 ts sackscalable wscale:8,9 rto:277 rtt:74/1 ato:40 cwnd:36 send 5.6Mbps rcv_space:5792

ฉันเรียกใช้เครื่องมือหลายครั้งและพบว่าขนาดหน้าต่างความแออัดถูกรีเซ็ตเป็นค่าเริ่มต้นเป็นประจำ (10ms บนกล่อง Linux ของฉัน) การเชื่อมต่อวนซ้ำอย่างต่อเนื่องไปยังเฟสเริ่มต้นที่ช้า ในช่วงระยะเวลาเริ่มต้นที่ช้า การส่งข้อความจำนวนมากเกินขนาดหน้าต่างจะเกิดความล่าช้า โดยรอแอกที่เกี่ยวข้องกับแพ็กเก็ตแรกของการแตก

ข้อเท็จจริงที่ว่าการจราจรประกอบด้วยลำดับของการระเบิด ซึ่งน่าจะอธิบายการรีเซ็ตขนาดหน้าต่างความแออัด

การปิดใช้งานโหมดเริ่มต้นช้าหลังจากไม่มีการใช้งาน ฉันสามารถกำจัดความล่าช้าได้

[[email protected] ~]$ cat /proc/sys/net/ipv4/tcp_slow_start_after_idle 0

นี่ไม่ใช่สิ่งที่ละเอียดอ่อนเช่นการตั้งค่าที่ไหนสักแห่ง นี่จะเป็นปัญหากับโปรโตคอลที่อยู่ด้านบนของ TCP หรือจุดบกพร่องของโค้ด ไม่มีสวิตช์วิเศษ "เร็วขึ้น" สำหรับ TCP ยกเว้นกรณีผิดปกติ เช่น เครือข่ายที่มีความหน่วงแฝงสูงมากหรือการสูญเสียแพ็กเก็ตที่เกิดจากเสียงรบกวน

คำอธิบายที่ชัดเจนที่สุดคือถ้าโค้ดเรียกwriteหรือsendส่วนย่อยๆ คุณต้องสะสมอย่างน้อย 2KB ต่อการส่งหนึ่งครั้ง เป็นการดีที่ 16KB คุณบอกว่าคุณจัดกลุ่มข้อความ แต่ไม่ชัดเจนว่ามันหมายถึงอะไร คุณส่งผ่านพวกเขาในการโทรครั้งเดียวไปที่writeหรือsend? คุณรวมพวกมันไว้ในหน่วยข้อมูลโปรโตคอลเดียวสำหรับโปรโตคอลที่อยู่ด้านบนของ TCP หรือไม่ การทำทั้งสองสิ่งนี้ช่วยได้มากในเรื่องเวลาในการตอบสนอง

นอกจากนี้ กำจัด TCP_NODELAY สามารถลดปริมาณงานได้ เฉพาะสำหรับแอปพลิเคชันที่ไม่ได้ออกแบบมาเพื่อทำงานกับ TCP หรือสำหรับแอปพลิเคชันที่ไม่สามารถคาดเดาได้ว่าจะต้องส่งด้านใดต่อไป

เว้นแต่ว่าจริง ๆ แล้วคุณกำลังวางโปรโตคอลทับบน TCP โดยที่คุณไม่รู้ว่าจะส่งต่อด้านใดต่อไป (เช่นtelnetตัวอย่างเช่น) จากนั้นจึงควรตั้งค่า TCP_NODELAY จำเป็นต้องมีความเชี่ยวชาญที่สำคัญเพื่อให้โปรโตคอลประเภทนั้นทำงานได้โดยมีเวลาแฝงต่ำ หากเป็นสถานการณ์ของคุณ ให้โพสต์รายละเอียดเพิ่มเติมเกี่ยวกับโปรโตคอลที่คุณกำลังเลเยอร์บน TCP ขนาดหน่วยข้อมูลโปรโตคอลมีลักษณะอย่างไร และอะไรเป็นตัวกำหนดว่าฝ่ายใดจะส่งเมื่อใด

หากคุณทำการแบทช์ข้อความที่มีอยู่ในคราวเดียว และส่งผ่านในการโทรครั้งเดียวไปที่writeหรือsendปัญหาส่วนใหญ่ก็คืออีกด้านหนึ่งไม่ได้ส่งการตอบรับระดับแอปพลิเคชันสำหรับแต่ละแบทช์ สิ่งเหล่านี้ปรับปรุงเวลาแฝงโดยให้แพ็กเก็ต TCP ACKs แก่ piggyback โปรโตคอลของคุณควรรวมไว้เพื่อให้แน่ใจว่าด้านต่างๆ จะสลับกัน ซึ่งช่วยลดเวลาในการตอบสนอง