Ist ThreadLocal sicher mit Tomcat NIO Connector zu verwenden

8

Das ist mir gerade in den Sinn gekommen, als ich den Tomcat NIO-Stecker während meiner Belastungstests getestet habe. Ich nutze ThreadLocal zusätzlich, ich benutze Spring, die ich an mehreren Stellen kenne, wo es auch benutzt wird.

Da der NIO-Konnektor keinen Thread pro Verbindung hat, mache ich mir Sorgen, dass es sehr schwierig ist, Fehler zu finden, wenn ein ThreadLocal-Objekt mit einem anderen Thread geteilt wurde, bevor es bereinigt wurde. Ich nehme jedoch an, dass dies kein Problem ist, da es sich nicht um eine dokumentierte Warnung handelt, die ich finden könnte, noch habe ich andere Artikel gefunden, die davor warnen. Ich nehme an, dass der NIO-Connector keine Auswirkungen auf die Threads hat, die die tatsächlichen Anforderungen erfüllen.

Bevor ich mit dieser Annahme fortfuhr, hoffte ich, einen konkreten Beweis zu finden.

    
David 28.10.2011, 04:02
quelle

3 Antworten

7

Nur jemand, der mit dem Tomcat-Code vertraut ist, kann Ihnen eine konkrete Antwort geben, aber ich werde einen hölzernen versuchen:)

Zuerst müssen Sie sich darüber im Klaren sein, ob Sie nur NIO-Konnektoren verwenden oder ob Sie auch über Async-Servlets sprechen. Die Antwort wird in jedem Fall etwas anders sein.

Die wichtigste Tatsache ist, dass Java keine Fortsetzungen, Co-Routinen oder Thread-Umplanungen hat. Dies bedeutet, dass nach dem Start eines Codeabschnitts, der auf einem Thread ausgeführt wird, nur dieser Codeabschnitt im Thread ausgeführt wird, bis er abgeschlossen ist.

Wenn Sie also: myObject.doSomething(); haben, dann hat der Zugriff auf den Thread doSomething für die Zeit, in der doSomething läuft. Der Thread wird nicht zu einem anderen Code wechseln - unabhängig davon, welches E / A-Modell Sie verwenden.

Was möglicherweise passieren wird, ist, dass verschiedene Threads für die Ausführung auf verschiedenen CPUs geplant sind, aber jeder Thread wird einen Code bis zum Ende ausführen.

Also, wenn doSomethingElse ist:

%Vor%

Dann müssen Sie sich keine Sorgen machen - service führt einen einzelnen Thread aus und der Threadlocal wird auf den richtigen Wert für die gesamte Ausführung gesetzt.

Ein einfacher NIO-Connector sollte also keinen Unterschied machen - der Container wird die Methode %code% auf dem Servlet aufrufen, das Servlet wird in einem einzigen Thread ausgeführt, und am Ende ist alles fertig. Es ist nur so, dass der Container in der Lage ist, den IO effizienter zu verarbeiten, da er die Verbindungen behandelt.

Wenn Sie asynchrone Servlets verwenden, ist das ein wenig anders - in diesem Fall könnte Ihr Servlet mehrmals für eine einzelne Anfrage aufgerufen werden (wegen der Art und Weise, wie das asynchrone Modell funktioniert), und diese Aufrufe können sich auf verschiedenen Threads befinden. Sie können also in einem Thread-Local zwischen den Aufrufen Ihres Servlets nichts speichern. Aber für einen einzigen Anruf zu Ihrer Service-Methode ist es immer noch in Ordnung.

HTH.

    
Tim 28.10.2011, 06:03
quelle
3

Um zu bestätigen, das ist immer noch ein Thread, der eine Anfrage bearbeitet, wie Sie es überprüfen können hier von der Tomcat-Mailingliste

    
rguitter 30.07.2013 13:50
quelle
1

Um die akzeptierte Antwort von Tim und die Folgefrage von pacman hinzuzufügen, müssen Sie vorsichtig sein, wenn Sie die AsyncResponse- oder ähnliche Funktion zusammen mit dem NIO-Connector verwenden. Ich bin nicht sicher, was Tim bedeutet, "Ihr [async] Servlet wird möglicherweise mehrmals für eine einzelne Anfrage aufgerufen" ... aber wenn eine "Anfrage" sich auf eine einzelne "GET", "PUT", "POST" bezieht oder "DELETE" und dann AFAIK, die zu einem einzelnen Aufruf der entsprechenden Ressourcenmethode in Ihrem Servlet führen.

Ein Problem, auf das Sie mit ThreadLocals und asynchronen Ressourcen stoßen könnten, ist, wenn der Verarbeitungs-Thread in der asynchronen Ressource eine Kopie der ThreadLocal-Variable aus der NIO-Ereignisschleife Thread benötigt. Mit anderen Worten, die NIO-Ereignisschleife Thread akzeptiert eine Anfrage und übergibt dann die Kontrolle an Ihre asynchrone Ressource ... dann übergibt diese Ressource die Kontrolle an einen untergeordneten Thread ... dann ist die NIO-Ereignisschleife Thread frei, um eine andere Anfrage zu bearbeiten ... so Alle ThreadLocal-Variablen in der NIO-Ereignisschleife Thread könnte durch die nachfolgende Anforderung gestempelt werden.

Beachten Sie, dass es auch möglich ist, dass jede neue Anfrage eine neue Instanz des Objekts im ThreadLocal speichert ... in diesem Fall wird jede neue Anfrage nicht auf die alten Instanzen stampfen, die vorher im selben ThreadLocal gespeichert wurden Anfragen ... aber Sie müssen sicher sein, mit welchem ​​Fall Sie es zu tun haben ... schauen wir uns einige Beispiele an.

Die ursprüngliche Frage bezieht sich auf Spring. Ein gutes Beispiel ist RequestContextHolder mit einem ThreadLocal. Nehmen wir an, die NIO-Ereignisschleife Thread heißt "http-nio-8080-exec-1" und übergibt die Kontrolle an eine AsyncResponse-Ressource, die dann einen neuen Thread ("pool-2-thread-3" genannt) über einen Executor startet . Der neue Thread hat Code, der etwas von den RequestAttributes benötigt, um die Antwort über AsyncResponse.resume () zu erhalten. Da der Code, der im Thread "pool-2-thread-3" ausgeführt wird, auf die RequestAttributes von "http-nio-8080-exec-1" zugreifen muss, müssen Sie zwei Dinge beachten:

1) Ihre Ressource ruft einen Verweis auf die RequestAttributes von "http-nio-8080-exec-1" ab und übergibt ihn an "pool-2-thread-3"

2) Wenn "http-nio-8080-exec-1" eine neue Anfrage annimmt, erstellt es eine neue Kopie von RequestAttributes und stellt diese in die ThreadLocal-Kopie für RequestContextHolder für die neue Anfrage ein (Hinweis: Spring code funktioniert Weg, so ist es sicher).

Ein gegenteiliges Beispiel ist die log4j MDC ThreadLocal-Kopie der Map. In diesem Fall verwendet jede neue Anfrage die gleiche Map ... also ist es nicht sicher, die Referenz der Map vom NIO Event Loop Thread an den AsyncResponse Thread zu übergeben ... Sie müssen eine Kopie der Map erstellen und diese übergeben. Siehe MDCAwareThreadPoolExectutor für ein Beispiel, wie man das macht.

Grundsätzlich müssen Sie jede ThreadLocal-Variable, die Sie von der NIO-Ereignisschleife Thread in Ihre AsyncResponse-Thread übergeben müssen ... und überprüfen, ob es sicher ist, nur einen Verweis auf das ursprüngliche Objekt übergeben, oder wenn Sie müssen eine Kopie des Objekts erstellen, bevor Sie die Kopie in die ThreadLocal-Variable des Worker-Threads setzen.

BTW, hier ist ein Code, der die beiden obigen Beispiele kombiniert:

%Vor%

Rufen Sie von Ihrer AsyncResponse-Ressource einfach wie folgt auf:

%Vor%     
Adam 13.10.2016 18:52
quelle

Tags und Links