2014-02-14 13 views
6

Die Situation: Ich habe eine Proxy-Anwendung, die Netty 4.0.17.Final verwendet (FYI: Ich hatte bereits das Problem mit den Versionen 4.0.13.Final und 4.0.9.Final) und das basiert auf der proxy from the Netty examples.Netty EventExecutorGroup bricht Pipeline

Der Hauptunterschied zwischen meinem Code und dem Beispiel ist, dass mein Code keine Verbindung zum Backend-Server herstellt, wenn der Kanal aktiv wird, sondern nur beim ersten Lesen, da dieser zuerst einige Eingaben überprüfen muss und Weiterleiten dieser Nachricht an den Backend-Server.

Ich habe Gerät getestet und meine App stundenlang getestet und es funktioniert gut.

Das Problem: Als die erste Nachricht Bedürfnisse erhielt einige Sperroperation auszuführen ich eine separate EventExecutorGroup für diesen einen Handler zu verwenden versucht, so macht, dass (so dass die IO-Threads blockiert werden nicht wichtig):

Dies (= die einzige Änderung, die ich gemacht habe!) Bricht die Anwendung während Belastungstests. Was bricht? XXX von 3500 Client-Verbindungen berichten mir, dass YY von 500 Nachrichten für diese Clients keine Antwort vom Proxy erhalten haben (jede Anfrage sollte eine Antwort bekommen). Ein Auszug aus den Client-Protokollen:

2014-02-14 00: 39: 56,146 [id: 0x34cb2c60] Fehler (com.nsn.ucpsimulator.common.UcpDecoder) - Idle-Verbindung (/127.0.0.1:7201). PDUs empfangen: 13

2014-02-14 00: 39: 56,146 [id: 0xf0955993] Fehler (com.nsn.ucpsimulator.common.UcpDecoder) - Idle-Verbindung (/127.0.0.1:7201). PDUs empfangen: 13

2014-02-14 00: 39: 56,147 [id: 0x9a911fa3] Fehler (com.nsn.ucpsimulator.common.UcpDecoder) - Idle-Verbindung (/127.0.0.1:7201). PDUs empfangen: 13

2014-02-14 00: 39: 56,149 [id: 0x811bbadf] Fehler (com.nsn.ucpsimulator.common.UcpDecoder) - Idle-Verbindung (/127.0.0.1:7201). PDUs empfangen: 13

2014-02-14 00: 39: 56,150 [id: 0x0c4d4c5a] Fehler (com.nsn.ucpsimulator.common.UcpDecoder) - Idle-Verbindung (/127.0.0.1:7201). PDUs erhalten: 13

Der Proxy-App sagt mir, dass 500 Nachrichten empfangen wurden und weitergeleitet, aber das nur 13 Antworten empfangen wurden und zurück an den Client weitergeleitet:

2014-02-14 00 : 39: 57.683 [ID: 0x39af563b] FEHLER (be.demmel.fun.UcpDecoder) - Leerlaufverbindung (/127.0.0.1:49359). PDUs erhalten: 500

2014-02-14 00: 39: 57,683 [id: 0x82056d39] Fehler (be.demmel.fun.FrontendHandler) - Idle-Verbindung (/127.0.0.1:52004), es Schließen . PDUs weitergeleitet: 500. Erfolg: 500

2014-02-14 00: 40: 00,717 [id: 0xcdca8f66] Fehler (be.demmel.fun.UcpDecoder) - Idle-Verbindung (/127.0.0.1:7900) .PDUs empfangen: 13

2014-02-14 00: 40: 00,718 [id: 0xcdca8f66] Fehler (be.demmel.fun.BackendHandler) - Idle-Verbindung (/127.0.0.1:7900). PDUs weitergeleitet: 13. Erfolg: 13

Der Server sagt mir, dass alles gut ist:

2014-02-14 00: 40: 02,855 [id: 0x4980be2c] ERROR (com.nsn .ucpsimulator.common.UcpDecoder) - Leerlaufverbindung (/127.0.0.1:37944). PDUs erhalten: 500

2014-02-14 00: 40: 02,856 [id: 0x4980be2c] Fehler (com.nsn.ucpsimulator.server.TestUcpHandler) - Idle Verbindung (/127.0.0.1:37944). PDUs zurückgeschickt: 500

Weiß jemand, was das verursachen könnte?

Zusätzliche Informationen:

  • beachten Sie, dass alles funktioniert, bis ich mit einem separaten EventExecutorGroup für die Blockierung Handler starten.

  • Jedes Mal, wenn XX-Clients blockieren, blockieren sie alle die gleiche Anzahl an Antworten, die an den Client weitergeleitet werden.

  • ich den netty Code hier hochgeladen haben (es runnable ist, enthält die Proxy-Server und Client-Anwendungen mit einer README): https://github.com/AndrewBourgeois/ucp-proxy/tree/master/src/main/java/be/demmel/fun

  • Wenn der Proxy-App getötet wird, dieser Fehler auf der Server-Seite erscheint :


java.io.IOException: Connection reset by peer 
    at sun.nio.ch.FileDispatcherImpl.read0(Native Method) ~[na:1.7.0_45] 
    at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39) ~[na:1.7.0_45] 
    at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223) ~[na:1.7.0_45] 
    at sun.nio.ch.IOUtil.read(IOUtil.java:192) ~[na:1.7.0_45] 
    at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:379) ~[na:1.7.0_45] 
    at io.netty.buffer.UnpooledUnsafeDirectByteBuf.setBytes(UnpooledUnsafeDirectByteBuf.java:401) ~[netty-all-4.0.9.Final.jar:na] 
    at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:869) ~[netty-all-4.0.9.Final.jar:na] 
    at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:208) ~[netty-all-4.0.9.Final.jar:na] 
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:87) ~[netty-all-4.0.9.Final.jar:na] 
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:478) ~[netty-all-4.0.9.Final.jar:na] 
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:447) ~[netty-all-4.0.9.Final.jar:na] 
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:341) ~[netty-all-4.0.9.Final.jar:na] 
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101) [netty-all-4.0.9.Final.jar:na] 
    at java.lang.Thread.run(Thread.java:744) [na:1.7.0_45] 

ich glaube, dass dieser Fehler, dass sind meine Netty Handler zeigt die Server-Antworten nicht verarbeiten.

+0

Haben Sie das jemals gelöst? Ich habe ein ähnliches Problem. – user1019182

Antwort

0

Ich denke, Ihr Problem ist, dass Sie eine neue DefaultEventExecutorGroup erstellen (10) alles, was Sie den Handler hinzufügen. Sie sollten es nur einmal erstellen und die die Instanz übergeben in

+0

Nein. Ich habe einen Fehler gemacht, als ich den Code für diesen Beitrag vereinfacht habe, das ist alles. Ich habe das nur in meiner Frage behoben. Das Testprojekt auf github hat den richtigen Code. Würden Sie die 2 Minuten brauchen, um dies zu reproduzieren, wenn ich Ihnen den Client- und Server-Code gab (Sie müssen nur mvn exec: java)? – AndrewBourgeois

+0

sicher ... einfach den Link gimme und ich werde überprüfen –

+0

https://github.com/AndrewBourgeois/ucp-proxy. Ich habe ein kleines README hinzugefügt. PS: Dieser Code reproduziert auch oft https://github.com/netty/netty/issues/2086 (was behoben ist, aber noch nicht veröffentlicht);) – AndrewBourgeois

1

einen Blick auf Ihre Github-Projekt zu haben, Ihre Ausführung ein wenig wie folgt aussieht:.

--> serve request 
    --> authenticate (blocking db call) 
    --> forward request 
    <-- receive response 
<-- serve response 

Ohne einen separaten EventExecutorGroup alle Ihre Ausführung innerhalb läuft die NioEventLoopGroup, die nur für nicht blockierende Aktionen verwendet werden soll. Jede gediente Anforderung würde decodieren und dann sofort den DB-Aufruf blockieren, sodass Ihr Server effektiv auf die Anzahl der Threads in der NioEventLoopGroup beschränkt ist.

Sie haben einen DefaultEventExecutorGroup um die ChannelHandler hinzugefügt, die die Authentifizierung der Fall ist, so jetzt Anfragen und Authentifizieren dienen, sind teilweise entkoppelt, da jede Anforderung decodiert wird, und dann wird die Ausführung an den DEEG weitergegeben werden, die NioEventLoopGroup verlassen mehr Anfragen zu dekodieren.

b.group(inboundChannel.eventLoop()) 

Was bedeutet, dass Sie immer noch mit Ihrer Blockierung DB-Verbindung Ihr Haupt netty Worker-Threads sind blockiert:

Mit Ausnahme der Bootstrap die dem DB verbindet, ist die gleiche NioEventLoopGroup als Ausgangskanal konfiguriert.

Ich bin mir nicht sicher, was nach diesem Punkt passiert, aber vielleicht Ihre eine Reihe von Anfragen zu dienen (effektiv Schlange sie alle warten auf die DEEG verfügbar zu sein) und dann sie zu timing, weil sie alle auf die warten blocking DB-Aufruf (die ihre Fähigkeit hat, ausgeführt werden verhungert, weil es mit Server-Dekodierungszeugnissen streitet).

dh (Vorausgesetzt, dass Sie viele Clients gleichzeitig haben)

[Original, 2 Gewinde NioEventLoopGroup, kein EventExecutorGroup]

nio-thread-1: serve-request 1 and authenticate (block) 
nio-thread-2: serve-request 2 and authenticate (block) 

(db calls completes) 

nio-thread-1: forward-request 1 (non-blocking) 
nio-thread-2: forward-request 2 (non-blocking) 

nio-thread-1: serve-request 3 and authenticate (block) 
nio-thread-2: serve-request 4 and authenticate (block) 

(db calls complete) 

nio-thread-1: forward-request 3 (non-blocking) 
nio-thread-2: forward-request 4 (non-blocking) 

nio-thread-1: either serve-response 1/2 or serve-request 5 (and block) 
nio-thread-2: either serve-response 1/2 or serve-request 6 (and block) 

Es ist nicht schön, aber Sie sind auf die Verarbeitung ungefähr n * begrenzt 2 Anfragen zu einer Zeit, unter der Annahme, Server-Anfragen und Server-Antworten werden mit der gleichen Dringlichkeit behandelt.

[2 Gewinde NioEventLoopGroup, 2 Gewinde DefaultEventExecutorGroup]

nio-thread-1: serve-request 1 and pass to DEEG 
nio-thread-2: serve-request 2 and pass to DEEG 
nio-thread-1: serve-request 3 and pass to DEEG 
nio-thread-2: serve-request 4 and pass to DEEG 
nio-thread-1: serve-request 5 and pass to DEEG 
nio-thread-2: serve-request 6 and pass to DEEG 
nio-thread-1: serve-request 7 and pass to DEEG 
nio-thread-2: serve-request 8 and pass to DEEG 

def-evt-eg-1: try to authenticate, pass execution back to nio-thread-x 
def-evt-eg-2: try to authenticate, pass execution back to nio-thread-x 

nio-thread-1: serve-request 9 and pass to DEEG 
nio-thread-2: serve-request 10 and pass to DEEG 
nio-thread-1: serve-request 11 and pass to DEEG 
nio-thread-2: serve-request 12 and pass to DEEG 
nio-thread-1: authenticate against DB (block) 
nio-thread-2: serve-request 12 and pass to DEEG 
nio-thread-2: serve-request 13 and pass to DEEG 
nio-thread-2: serve-request 14 and pass to DEEG 
nio-thread-2: serve-request 15 and pass to DEEG 
nio-thread-2: authenticate against DB (block) 

Jetzt sind in der Lage Sie mehr Anfragen verbrauchen, aber die Geschwindigkeit, mit der Sie machen DB Anrufe und die Gesamtlatenzzeit über den Server von der Anzahl abhängig der Anzahl gleichzeitiger Clients, Anzahl der DEEG-Threads v Anzahl der NioEventLoop-Threads, Kontextwechsel usw.

Sie können dies wahrscheinlich visualisieren, indem Sie beim Ausführen Ihrer App einige grundlegende Threaddiagnosen ausdrucken. Ich könnte völlig falsch liegen, da ich keine Chance hatte, es zu tun und zu sehen, das ist nur meine Vermutung.

+0

Während Ihr Punkt gültig ist (das Blockieren von Nettys IO-Threads ist schlecht), glaube ich nicht, dass dies die Ursache meines Problems ist. Die Authentifizierung findet nur für die erste PDU statt, nach der Tausende von PDUs nicht blockiert werden. Ihr zweites Beispiel zeigt, warum ich ein DEEG verwenden möchte. Das EventLoop-Sharing-Ding ist etwas, was ich nachschlagen werde. – AndrewBourgeois

+0

Hey Andrew, bist du am Ende zu einer Lösung gekommen? Ich wäre interessiert zu wissen, was das Problem sein würde. –

+0

@Norman Maurer wird mein Projekt laufen lassen, um zu sehen, ob er das Problem reproduzieren und verstehen kann (überprüfe meine Kommentare zu seiner Antwort). Das Überprüfen des Blockierungsproblems ist momentan nicht meine Priorität (es ist nicht Teil des Codes, den ich hochgeladen habe (und dieser Code reproduziert mein Problem und schließt deine Antwort aus)). – AndrewBourgeois