שאלה הבהרה לגבי גודל חלון TCP ועיכובים


יש לי עיכובים בעת שליחת נתונים דרך ערוץ TCP אני לא מסוגל להבין. הקישור הוא קישור 1Gb עם חביון סוף עד סוף של כ 40ms. בהגדרה הנוכחית שלי, חביון (הזמן מתוך הודעה אחת לעבור ממרחב המשתמש השולח למרחב המשתמש של המקלט) יכול להגיע ל -100ms.

שקע השולח מוגדר עם האפשרות TCP_NODELAY. המאגר חיישן (SO_SNDBUF) מוגדר להיות 8MB. מקבל מאגר (SO_RCVBUF) מוגדר גם להיות 8MB. שינוי קנה המידה חלון TCP.

עדכון 1: אני משתמש תווכה zeromq 3.1.1 לשאת נתונים. תצורת Socket, כולל הדגל TCP_NODELAY מבוצעת על ידי תווכה. כמה אפשרויות נגישים כמו rx ו tx emit גודל חיץ אבל לא TCP_NODELAY. ככל שהבנתי, ה- TCP_NODELAY מופעל כדי להבטיח שהנתונים יישלחו ככל האפשר. בינתיים, שקע בפועל שולח והחלטה לשלוח הודעה מבוצעים בשני נושאים נפרדים. A batching ראוי נעשה אם כמה הודעות זמינות בזמן ההודעה הראשונה אצווה היא להישלח.

אני רץ ללכוד עם tcpdump שממנו מסגרות למטה כבר חולץ. לאחר לחיצת היד הראשונה של TCP, השולח (172.17.152.124) מתחיל לשלוח נתונים. גודל החלון הראשוני הוא 5840 בתים עבור המקלט & 5792 בתים עבור השולח.

הבעיה שלי היא כי שולח שולח שתי מסגרות (# 6 ו # 7) ואז מפסיק, מחכה ack לחזור מן המקלט. ככל שאני יכול לראות, גודל החלון של המקלט לא הגיע והעברת לא צריך להפסיק (384 בתים מצטיינים עם ראשוני לקבל גודל חלון של 5840 בתים). אני מתחיל לחשוב כי לא הבנתי נכון מה TCP. מישהו יכול לעזור להבהיר?

עדכון 2: מטען הנתונים שלי מורכב ממספר קסום ואחריו חותמת. אני מבודד את מנות מאוחרת על ידי השוואת חותמות הזמן של המטען עם חותמות על ידי tcpdump. את המטען TS של מסגרת # 9 הוא קרוב מאוד אחד המסגרת # 6 ו # 7 ו בבירור פחות חותמת של ack קיבל במסגרת # 8.

עדכון 1: העובדה שמסגרת # 9 לא נשלחת מיד יכולה להיות מוסברת על ידי התחלה איטית של ערוץ TCP. למעשה, הבעיה מופיעה גם כאשר החיבור פועל במשך מספר דקות, כך שההתחלה האיטית אינה נראית ההסבר הכללי.

  1. 20: 53: 26.017415 IP 172.17.60.9.39943> 172.17.152.124.56001: דגלים [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: דגלים [S.], seq 2948065596, ack 2473022772, win 5792, options [mss 1460, sackOK, TS val 186598852 ecr 219180820, nop, wscale 9 ], אורך 0

  3. 20: 53: 26.091940 IP 172.17.60.9.39943> 172.17.152.124.56001: דגלים [.], Ack 1, win 23, options [nop, nop, TS val 4219180894 ecr 186598852], אורך 0

  4. 20: 53: 26.091958 IP 172.17.60.9.39943> 172.17.152.124.56001: דגלים [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: דגלים [.], Ack 15, win 12, options [nop, nop, TS val 186598927 ecr 4219180895], אורך 0

  6. 20: 53: 26.128298 IP 172.17.152.124.56001> 172.17.60.9.39943: דגלים [P.], seq 1: 257, ack 15, win 12, options [nop, nop, TS val 186598963 ecr 4219180895], אורך 256

  7. 20: 53: 26.128519 IP 172.17.152.124.56001> 172.17.60.9.39943: דגלים [P.], 257: 385, ack 15, win 12, options [nop, nop, TS val 186598963 ecr 4219180895], אורך 128

  8. 20: 53: 26.202465 IP 172.17.60.9.39943> 172.17.152.124.56001: דגלים [.], Ack 257, win 27, options [nop, nop, TS val 4219181005 ecr 186598963], אורך 0

  9. 20: 53: 26.202475 IP 172.17.152.124.56001> 172.17.60.9.39943: דגלים [.], סק 385: 1833, ack 15, win 12, options [nop, nop, TS val 186599037 ecr 4219181005], אורך 1448

  10. 20: 53: 26.202480 IP 172.17.152.124.56001> 172.17.60.9.39943: דגלים [P.], 1833: 2305, ack 15, win 12, options [nop, nop, TS val 186599037 ecr 4219181005], אורך 472

אם זה משנה, שתי הקצוות הן לינוקס RHEL5 תיבות, עם 2.6.18 kernels וכרטיסי רשת משתמשים מנהלי התקנים e1000e.

עדכון 3  תוכן של /etc/sysctl.conf

[jlafaye@localhost ~]$ 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

6
2018-05-07 21:08




איך בדיוק אתה יודע כי מנות 9 היה צריך להישלח מיד אחרי 6/7? אני שם לב שזה לא דגל PUSH להגדיר. - pjc50
אנחנו יכולים לראות את /etc/sysctl.conf? - ewwhite
כרטיס נערך עם התוכן של /etc/sysctl.conf - jlafaye


תשובות:


אחרי שעשיתי קצת יותר חפירה בתנועה שלי, יכולתי לראות שהנתונים שלי אינם אלא רצף של התפרצויות קטנות עם תקופות סרק קטנות ביניהן.

עם כלי שימושי ss, הצלחתי לאחזר את גודל הגודש הנוכחי גודל של החיבור שלי (ראה cwnd ערך בפלט):

[user @ localhost ~] $ / usr / sbin / ss -i -t -e | grep -A 1 56,001

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

רצתי את הכלי מספר פעמים וגיליתי כי גודל חלון הגודש היה לאפס באופן קבוע לערך הראשוני (10ms, על תיבת לינוקס שלי). החיבור היה כל הזמן לולאה חזרה לשלב ההתחלה האיטית. במהלך תקופת ההתחלה האיטית, התפרצויות עם מספר מסרים העולים על גודל החלון התעכבו, מחכים ל- acks הקשורים למנות הראשונות של ההתפרצות.

העובדה כי התנועה מורכבת רצף של התפרצויות סביר להסביר את האיפוס של גודל חלון הגודש.

על ידי ביטול מצב התחלה איטית לאחר תקופת המתנה, הייתי מסוגל להיפטר העיכובים.

[user @ host ~] $ cat / proc / sys / net / ipv4 / tcp_slow_start_after_idle   0


10
2018-05-09 20:48



יישומי שכבת יישומים היו פותרים את כלי הבעיה (על-ידי החזרת ה- ACK בחזרה מהר יותר) וזה לא היה דורש כוונון ברמת המערכת. (אם אתה תמיד יכול לכוונן את המערכות תוכנה זו פועלת, אז זה פשוט יותר, אבל אם אתה לא יכול, אתה צריך להוסיף תודות שכבת יישומים.) עבודה בלש נחמד, דרך אגב. - David Schwartz
עכשיו שאתה מזכיר את העבודה הבלשית. אני מרצון מחוץ חלקים מסוימים של החקירה, כולל אחד מפעיל ליבה על kvm והוספת gdb watchpoints על גודל חלון גודש הערך. - jlafaye
@ DavidSchwartz: למרות יישום שכבת acks היה פותר את הבעיה של OP, הם לא יפתור את כל המופעים של בעיה זו. היה לי זה קורה באמצעות שני ערוצי ActiveXQ סימפלקס שבו הערוצים שימשו לסירוגין עם ערוץ אחד תמיד סרק בזמן השני משמש. עד שהודעה הבאה נשלחה בערוץ נתון, חלון הגודש כבר נסגר בשל תקופת ההמתנה, ושום כמות של אפליקציות ברמת היישום לא היתה משנה זאת. אז ההצעה שלך היא טובה, אבל לא ישים על כל המצבים (ואילו הפתרון של OP J החלים בכל המקרים). - Tim


זה לא הולך להיות משהו מתוחכם כמו הגדרה איפשהו. זה הולך להיות בעיה עם פרוטוקול שכבות על גבי TCP או באג קוד. אין מתג הקסם "מהר יותר" עבור TCP למעט במקרים יוצאי דופן כמו רשתות עם חביון גבוה מאוד או אובדן מנות שנגרם על ידי רעש.

ההסבר הברור ביותר יהיה אם הקוד קורא write או send עם נתחים קטנים מאוד. אתה צריך לצבור לפחות 2KB לכל לשלוח, באופן אידיאלי 16KB. אתה אומר שאתה מסדר את ההודעות, אבל לא ברור מה זה אומר. אתה מעביר אותם לשיחה אחת write או send? האם אתה צרור אותם ליחידת נתונים אחת פרוטוקול עבור פרוטוקול שכבות על גבי TCP? עושה את שני הדברים האלה עוזר הרבה עם חביון.

כמו כן, להיפטר TCP_NODELAY. זה יכול להפחית את התפוקה. זה רק עבור יישומים שלא נועדו לעבוד עם TCP או עבור יישומים שאינם יכולים לחזות איזה צד יהיה צורך להעביר הבא.

אלא אם כן, כמובן, אתה בעצם שכבת פרוטוקול על גבי TCP שבו אתה לא יודע איזה צד עומד להעביר הבא (כמו telnet, לדוגמה). אז זה הגיוני להגדיר TCP_NODELAY. מומחיות משמעותית נדרש לעשות את זה סוג של עבודה בפרוטוקול עם חביון נמוך. אם זה המצב שלך, פרסם פרטים נוספים על הפרוטוקול שאתה שכבת על גבי TCP, מה גודלי הנתונים של פרוטוקול הנתונים שלה נראה כמו, ומה קובע איזה צד מעביר מתי.

אם אתה עושה למעשה אצווה את ההודעות הזמינות בו זמנית ולהעביר אותם בשיחה אחת ל write או send, סביר להניח שהבעיה היא שהצד השני אינו שולח אישור על שכבת יישום עבור כל אצווה. אלה לשפר את זמן האחזור על ידי מתן מנות ACPs TCP כדי piggyback ב. הפרוטוקול שלך צריך לכלול אותם כדי להבטיח צד חלופי אשר מסייע לשמור על חביון למטה.


2
2018-05-07 21:55



אני לא מסכים: אין שום דבר רע עם כותב קטן אם אתה רוצה שזה יישלח מיד. הוא מנסה לעצב מערכת חביון נמוכה מאוד. - pjc50
יש בהחלט. אם אתה כותב בעיה קטנה כאשר יש נתונים אחרים שאתה רוצה לשלוח מיד, אתה מעכב עיכוב את הנתונים שאתה לא לכלול בכתב קטן. (כמו בעיה של OP מוכיח.) - David Schwartz
הוא אמר "ראוי batching נעשה אם כמה הודעות זמינות בזמן ההודעה הראשונה אצווה היא להישלח". לא לדעת דבר על מקור הנתונים, אנחנו לא יכולים לומר אם נתונים חדשים ייתכן שיהיה צורך לשלוח מיד לאחר ששלחת רק מנות. - pjc50
לא ברור שפירוש הדבר שהמסרים מצטברים לשיחה אחת write או send. עם זאת, אם הם, אז הבעיה היא כנראה בעיצוב של פרוטוקול שכבות על גבי TCP - סביר להניח היעדר שכבות יישום הבקשה. אלה נותנים מנות ACKs כדי פיגי בחזרה על והם חיוניים עבור יישומים חביון נמוך. (התשובה עודכנה.) - David Schwartz