2016-06-20 6 views
2

Beim Versuch, eine große Anzahl von TCP-Verbindungen parallel aufzubauen, beobachte ich ein seltsames Verhalten, das ich für einen potentiellen Fehler in gen_tcp halte.GESCHLOSSENER Fehler beim parallelen Herstellen einer großen Anzahl von Verbindungen mit gen_tcp (Bug?)

Das Szenario ist ein Server, der auf einem Port mit mehreren gleichzeitigen Akzeptoren abhört. Von einem Client aus stelle ich eine Verbindung her, indem ich gen_tcp:connect/3 anrufe, danach sende ich eine "Ping" -Nachricht an den Server und warte im passiven Modus auf eine "Pong" -Antwort. Bei der sequenziellen Ausführung von 'get_tcp: connect/3' funktioniert alles gut, auch für eine große Anzahl von Verbindungen (ich habe bis zu ~ 28000 getestet).

Das Problem tritt auf, wenn versucht wird, viele Verbindungen parallel aufzubauen (abhängig von der Maschine zwischen ~ 75 und mehreren hundert). Während die meisten Verbindungen noch hergestellt werden, scheitern einige Verbindungen mit einem closed Fehler in gen_tcp:recv/3. Das Seltsame ist, dass diese Verbindungen nicht vorher versagten, die Aufrufe an gen_tcp:connect/3 und gen_tcp:send/2 waren beide erfolgreich (d. H. Zurückgegeben ok). Auf der Serverseite sehe ich keine passende Verbindung für diese "seltsamen" Verbindungen, d. H. Keine Rückkehr gen_tcp:accept/1. Es ist mein Verständnis, dass eine erfolgreiche 'get_tcp: connect/3' zu einer übereinstimmenden akzeptierten Verbindung auf der Serverseite führen sollte.

Ich habe bereits eine bug report eingereicht, dort finden Sie eine detailliertere Beschreibung und ein minimales Codebeispiel, um das Problem zu demonstrieren. Ich konnte das Problem unter Linux und Mac OS X und mit verschiedenen Erlang-Versionen reproduzieren.

Meine Frage lautet hier:

  1. Ist jemand in der Lage, das Problem zu reproduzieren und bestätigen kann, dass diese fehlerhaftes Verhalten ist?
  2. Irgendwelche Ideen für eine Problemumgehung? Wie geht man mit diesem Problem um, andere starten alle Verbindungen nacheinander (was ewig dauert)?
+3

Ohne auf das Beispiel zu schauen, mein erster Gedanke ist, dass die tcp 'backlog' Größe möglicherweise zu klein ist. Hast du versucht es zu erhöhen? (http://erlang.org/doc/man/gen_tcp.html#listen2) Siehe http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux .html für eine Beschreibung dessen, was es tut. – johlo

+0

Ich stimme @johlo zu. Ich habe Ihren Code untersucht und Sie verwenden nicht die Option "{backlog, B}". Mögliches Duplikat von [Erlang TCP Sockets wird geschlossen] (http://stackoverflow.com/questions/32511676/erlang-tcp-sockets-get-closed) –

+0

@johlo Danke, toller Tipp und toller Link. Durch das Erhöhen des Backlogs wurde das Problem gelöst und der Artikel erklärt, warum der Aufruf von 'gen_tcp: connect/3' ohne Fehler zurückkehrt (weil er das SYN/ACK vom Server erhalten und das finale ACK gesendet hat), aber der Server nicht endet Status festgelegt (weil es die letzte ACK ignoriert hat). – jvf

Antwort

3

TCP 3-Wege-Handshake Client Server

connect()│──┐   │listen() 
      │ └──┐  │ 
      │  SYN │ 
      │  └──┐ │ 
      │   └▶│ STATE 
      │   ┌──│SYN-RECEIVED 
      │  ┌──┘ │ 
      │ SYN-ACK │ 
      │ ┌──┘  │ 
    STATE │◀┘   │ 
ESTABLISHED│──┐   │ 
      │ └──┐  │ 
      │  └ACK │ 
      │  └──┐ │ STATE 
      │   └▶│ESTABLISHED 
      ▽    ▽ 

Das Problem liegt bei den feineren Details des 3-Wege-Handshake für eine TCP-Verbindung und die Warteschlange für eingehende Verbindungen an der Steckdose hören zu etablieren. Sehen Sie diese excellent article für Details, viele der folgenden Erklärung wurde von diesem Artikel informiert.

In Linux gibt es tatsächlich zwei Warteschlangen für eingehende Verbindungen. Wenn der Server eine Verbindungsanforderung (SYN Paket) empfängt und in den Status SYN-RECEIVED übergeht, wird diese Verbindung in die Warteschlange SYN versetzt. Wenn ein entsprechender ACK empfangen wird, werden die Verbindungen in die Annahmeschlange gestellt, damit die Anwendung verwendet werden kann. Die {backlog, N} (Standardeinstellung: 5) -Option zu gen_tcp:listen/2 bestimmt die Länge der Zugriffswarteschlange.

Wenn der Server eine ACK empfängt, während die Annahmewarteschlange voll ist, wird die ACK grundsätzlich ignoriert und keine RST wird an den Client gesendet. Mit dem Status SYN-RECEIVED ist ein Timeout verbunden: Wenn kein ACK empfangen (oder ignoriert, wie hier), wird der Server die SYN-ACK erneut senden. Der Client sendet dann erneut die ACK.Wenn die Anwendung einen Eintrag aus der Annahmeschlange verbraucht, bevor die maximale Anzahl der Wiederholungen SYN-ACK erreicht wurde, verarbeitet der Server möglicherweise eines der Duplikate ACKs und wechselt in den Status ESTABLISHED. Wenn die maximale Anzahl an Wiederholungen erreicht wurde, sendet der Server eine RST an den Client, um die Verbindung zurückzusetzen.

Zurück zu dem Verhalten, das beim Starten vieler paralleler Verbindungen beobachtet wird. Die Erklärung ist, dass die Annahmewarteschlange auf dem Server schneller voll wird als unsere Anwendung die akzeptierten Verbindungen verbraucht. Die gen_tcp:connect/3 Aufrufe auf der Clientseite werden erfolgreich zurückgegeben, sobald die ersten SYN-ACK empfangen werden. Die Verbindungen werden nicht sofort zurückgesetzt, da der Server die SYN-ACK erneut versucht. Der Server meldet diese Verbindungen nicht als erfolgreich, da sie sich immer noch im Status SYN-RECEIVED befinden.

Auf BSD abgeleitete System (einschließlich Mac OS X) funktioniert die Warteschlange für eingehende Verbindungen ein wenig anders, siehe oben genannten article.

Verwandte Themen