Ich habe Verzögerungen beim Senden von Daten über einen TCP-Kanal, den ich nicht verstehen kann. Die Verbindung ist eine 1-GB-Verbindung mit einer End-to-End-Latenz von ungefähr 40 ms. In meinem aktuellen Setup kann die Latenz (die Zeit von einer Nachricht vom Senderbenutzerbereich zum Empfängerbenutzerbereich) 100 ms erreichen.
Der Absendersocket wird mit der Option TCP_NODELAY konfiguriert. Der Senderpuffer (SO_SNDBUF) ist auf 8 MB konfiguriert. Der Empfangspuffer (SO_RCVBUF) ist ebenfalls auf 8 MB konfiguriert. Die TCP-Fensterskalierung ist aktiviert.
Update-1 : Ich verwende die Zeromq 3.1.1 Middleware, um Daten zu übertragen. Die Socket-Konfiguration einschließlich des TCP_NODELAY-Flags wird von der Middleware durchgeführt. Auf einige Optionen kann zugegriffen werden, z. B. rx und tx senden Puffergrößen aus, nicht jedoch TCP_NODELAY. Soweit ich verstanden habe, wird TCP_NODELAY aktiviert, um sicherzustellen, dass die Daten so gut wie möglich gesendet werden. In der Zwischenzeit werden die tatsächlichen Socket-Sendungen und die Entscheidung zum Senden einer Nachricht in zwei separaten Threads ausgeführt. Eine ordnungsgemäße Stapelverarbeitung erfolgt, wenn zum Zeitpunkt des Versendens der ersten Nachricht im Stapel mehrere Nachrichten verfügbar sind.
Ich habe mit tcpdump ein Capture ausgeführt, aus dem die folgenden Frames extrahiert wurden. Nach dem ersten TCP-Handshake beginnt der Absender (172.17.152.124) mit dem Senden von Daten. Die anfängliche Fenstergröße beträgt 5840 Byte für den Empfänger und 5792 Byte für den Absender.
Mein Problem ist, dass der Absender zwei Frames sendet (Nr. 6 und Nr. 7) und dann anhält und darauf wartet, dass eine Bestätigung vom Empfänger zurückkommt. Soweit ich sehen kann, wird die Fenstergröße des Empfängers nicht erreicht und die Übertragung sollte nicht gestoppt werden (384 Bytes ausstehend bei einer anfänglichen Empfangsfenstergröße von 5840 Bytes). Ich fange an zu denken, dass ich nicht richtig verstanden habe, was TCP ist. Kann jemand bei der Klärung helfen?
Update-2 : Meine Datennutzlast besteht aus einer magischen Zahl, gefolgt von einem Zeitstempel. Ich habe die verzögerten Pakete isoliert, indem ich die Zeitstempel der Nutzdaten mit den von tcpdump gesetzten Zeitstempeln verglichen habe. Die Nutzlast ts von Frame # 9 liegt sehr nahe an der von Frame # 6 und # 7 und deutlich unter dem Zeitstempel der empfangenen Bestätigung in Frame # 8.
Update-1 : Die Tatsache, dass Frame # 9 nicht sofort gesendet wird, kann durch den langsamen Start des TCP-Kanals erklärt werden. Tatsächlich tritt das Problem auch auf, wenn die Verbindung einige Minuten lang ausgeführt wird, sodass der langsame Start nicht die allgemeine Erklärung zu sein scheint.
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], Länge 0
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 ], Länge 0
20: 53: 26.091940 IP 172.17.60.9.39943> 172.17.152.124.56001: Flags [.], Ack 1, Win 23, Optionen [nop, nop, TS val 4219180894 ecr 186598852], Länge 0
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, Optionen [nop, nop, TS val 4219180895 ecr 186598852], Länge 14
20: 53: 26.091964 IP 172.17.152.124.56001> 172.17.60.9.39943: Flags [.], Ack 15, Win 12, Optionen [nop, nop, TS val 186598927 ecr 4219180895], Länge 0
20: 53: 26.128298 IP 172.17.152.124.56001> 172.17.60.9.39943: Flags [P.], Sequenz 1: 257, ack 15, win 12, Optionen [nop, nop, TS val 186598963 ecr 4219180895], Länge 256
20: 53: 26.128519 IP 172.17.152.124.56001> 172.17.60.9.39943: Flags [P.], Sequenz 257: 385, ack 15, win 12, Optionen [nop, nop, TS val 186598963 ecr 4219180895], Länge 128
20: 53: 26.202465 IP 172.17.60.9.39943> 172.17.152.124.56001: Flags [.], Ack 257, Win 27, Optionen [nop, nop, TS val 4219181005 ecr 186598963], Länge 0
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], Länge 1448
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], Länge 472
In diesem Fall handelt es sich bei beiden Enden um Linux-RHEL5-Boxen, wobei 2.6.18-Kernel und Netzwerkkarten e1000e-Treiber verwenden.
update-3 Inhalt von /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
quelle
/etc/sysctl.conf
?Antworten:
Nachdem ich mich ein wenig mehr mit meinem Datenverkehr befasst hatte, konnte ich feststellen, dass meine Daten nichts anderes als eine Folge kleiner Bursts mit kleinen Leerlaufzeiten zwischen ihnen waren.
Mit dem nützlichen Tool
ss
konnte ich die aktuelle Größe des Überlastungsfensters meiner Verbindung abrufen (siehe dencwnd
Wert in der Ausgabe):Ich habe das Tool mehrmals ausgeführt und festgestellt, dass die Größe des Überlastungsfensters regelmäßig auf den Anfangswert zurückgesetzt wurde (10 ms auf meiner Linux-Box). Die Verbindung kehrte ständig in die langsame Startphase zurück. Während der langsamen Startperiode wurden Bursts mit einer Anzahl von Nachrichten, die die Fenstergröße überschreiten, verzögert und warteten auf die Bestätigungen, die sich auf die ersten Pakete des Bursts beziehen.
Die Tatsache, dass der Verkehr aus einer Folge von Bursts besteht, erklärt wahrscheinlich das Zurücksetzen der Größe des Überlastungsfensters.
Durch Deaktivieren des langsamen Startmodus nach der Leerlaufzeit konnte ich die Verzögerungen beseitigen.
quelle
Dies wird keine subtile Sache wie eine Einstellung irgendwo sein. Dies wird ein Problem mit dem auf TCP geschichteten Protokoll oder ein Codefehler sein. Es gibt keinen magischen "schneller gehen" -Schalter für TCP, außer in ungewöhnlichen Fällen wie Netzwerken mit sehr hoher Latenz oder Paketverlust aufgrund von Rauschen.
Die naheliegendste Erklärung wäre, wenn der Code aufruft
write
odersend
mit sehr kleinen Blöcken. Sie müssen mindestens 2 KB pro Sendung akkumulieren, idealerweise 16 KB. Sie sagen, Sie stapeln die Nachrichten, aber es ist nicht klar, was das bedeutet. Übergeben Sie sie in einem Anruf anwrite
odersend
? Bündeln Sie sie in einer einzigen Protokolldateneinheit für das auf TCP geschichtete Protokoll? Beides hilft bei der Latenz sehr.Entfernen Sie auch TCP_NODELAY. Es kann den Durchsatz reduzieren. Dies gilt nur für Anwendungen, die nicht für die Verwendung mit TCP ausgelegt sind, oder für Anwendungen, die nicht vorhersagen können, welche Seite als Nächstes übertragen muss.
Es sei denn, Sie legen tatsächlich ein Protokoll auf TCP, bei dem Sie nicht wissen, welche Seite als nächstes senden wird (wie
telnet
zum Beispiel). Dann kann es sinnvoll sein, TCP_NODELAY zu setzen. Um diese Art von Protokoll mit geringer Latenz arbeiten zu können, ist erhebliches Fachwissen erforderlich. Wenn dies Ihre Situation ist, veröffentlichen Sie weitere Details zu dem Protokoll, das Sie über TCP legen, wie die Größe der Protokolldateneinheiten aussieht und was bestimmt, welche Seite wann überträgt.Wenn Sie die verfügbaren Nachrichten tatsächlich stapelweise gleichzeitig stapeln und in einem einzigen Anruf an
write
odersend
weiterleiten, besteht das Problem höchstwahrscheinlich darin, dass die andere Seite nicht für jeden Stapel eine Bestätigung auf Anwendungsebene sendet. Diese verbessern die Latenz, indem TCP-ACK-Pakete huckepack gegeben werden. Ihr Protokoll sollte sie enthalten, um sicherzustellen, dass sich die Seiten abwechseln, wodurch die Latenz gering gehalten wird.quelle
write
oder zusammengefasst werdensend
. Wenn dies jedoch der Fall ist, liegt das Problem wahrscheinlich im Entwurf des auf TCP geschichteten Protokolls - höchstwahrscheinlich im Fehlen von Bestätigungen auf Anwendungsebene. Diese geben ACKs-Pakete zum Huckepack und sind für Anwendungen mit geringer Latenz unerlässlich. (Antwort aktualisiert.)