Asynchronous-IO vs Asynchronous-Request-Verarbeitung in Java

In diesem Artikel versuche ich, den Unterschied zwischen Async-IO- und Async-Request-Verarbeitung in der HTTP-Anfrage in der Java-Welt zu erklären.

In der Welt vor Java 1.4 bietet Java eine API zum Senden / Empfangen von Daten über den Netzwerk-Socket. Die ursprünglichen Autoren von JVM haben dieses API-Verhalten fast eins zu eins der OS-Socket-API zugeordnet.

Wie verhält sich das Betriebssystem? Das Betriebssystem bietet eine Socket-Programmier-API, die einen Aufruf zum Senden / Empfangen blockiert . Da Java nur ein Prozess ist, der über Linux (OS) ausgeführt wird, muss dieses Java-Programm diese vom Betriebssystem bereitgestellte Blockierungs-API verwenden.

Die Welt war glücklich und Java-Entwickler begannen, die API zum Senden / Empfangen der Daten zu verwenden. Aber sie mussten einen Java-Thread für jeden Socket (Client) behalten.

Jeder schrieb seine eigene Variante von HTTP-Servern. Das Teilen von Code wurde immer schwieriger, die Java-Welt forderte eine Standardisierung.

Gibt das Java-Servlet Spec ein.

Bevor wir fortfahren, definieren wir einige Begriffe:

Java Server Developer : Personen, die die Java-Socket-API verwenden und das http-Protokoll wie Tomcat implementieren.

Java Application Developer: Personen, die eine Geschäftsanwendung auf Tomcat erstellen.

JETZT ZURÜCK

Sobald die Java-Servlet-Spezifikation in die Welt kam, hieß es:

Sehr geehrte Java-Server-Entwickler, bitte geben Sie eine Methode wie die folgende an:

doGet(inputReq, OutPutRes)

Damit Java-Anwendungsentwickler implementieren doGetund ihre Geschäftslogik schreiben können. Sobald "Anwendungsentwickler" das senden möchte response, kann er anrufenOutPutRes.write().

Zu beachten: Da die Socket-API blockiert, blockiert auch OutPutRes.write (). Die zusätzliche Einschränkung bestand auch darin, dass das Antwortobjekt beim Beenden der doGet-Methode festgeschrieben wird .

Aufgrund dieser Einschränkungen mussten Benutzer einen Thread zum Verarbeiten einer Anforderung verwenden.

Die Zeit verging und das Internet eroberte die Welt. Ein Thread pro Anforderung zeigte Einschränkungen an.

Problem 1:

Das Thread-per-Request-Modell schlägt fehl, wenn während der Verarbeitung jeder Anfrage lange Pausen auftreten.

Zum Beispiel: Das Abrufen von Daten vom Subdienst dauert lange.

In einer solchen Situation sitzt der Thread meistens im Leerlauf und JVM kann leicht der Thread ausgehen.

Problem 2:

Mit der dauerhaften http1.1-Verbindung wurde es noch schlimmer. Wie bei einer dauerhaften Verbindung bleibt die zugrunde liegende TCP-Verbindung am Leben und der Server muss einen Thread pro Verbindung blockieren .

Aber warum muss der Server einen Thread pro Verbindung blockieren ?

Da das Betriebssystem eine Blocking-Socket-Recv-API bereitstellt, muss die JVM die OS-Blockierungs-Recv-Methode aufrufen, um auf weitere Anforderungen auf derselben TCP-Verbindung vom Client zu warten.

Die Welt forderte eine Lösung!

Die erste Lösung kam vom Schöpfer von JVM. Sie führten NIO ( ASYNC-IO ) ein. Nio ist die nicht blockierende API zum Senden / Empfangen von Daten über Socket.

Einige Hintergrundinformationen: dieDas Betriebssystem bietet neben der blockierenden Socket-API auch eine nicht blockierende Version der Socket-API.

Aber wie stellt das Betriebssystem das bereit? Gabelt es einen Thread intern und dieser Thread wird blockiert?

Die ANTWORT ist nein ... das Betriebssystem weist die Hardware an, zu unterbrechen, wenn Daten zum Lesen oder Schreiben vorhanden sind.

NIO erlaubte dem " Java Server Entwickler"um das Problem 2 zu lösen , einen Thread pro TCP-Verbindung zu blockieren . Da NIO eine dauerhafte HTTP-Verbindung ist, muss es vom Thread nicht beim erneuten Aufruf blockiert werden . Stattdessen kann es jetzt nur verarbeitet werden, wenn Daten verarbeitet werden müssen. Dadurch konnte ein Thread eine große Anzahl von dauerhaften Verbindungen überwachen / verarbeiten.

Die zweite Lösung stammte aus der Servlet-Spezifikation. Servlet Spec erhielt ein Upgrade und führte die asynchrone Unterstützung ein (Async Request Processing).

AsyncContext acontext = req.startAsync();
WICHTIG: Durch dieses Upgrade wurde die Einschränkung aufgehoben , das Antwortobjekt beim Abschluss der doGet- Methode festzuschreiben .

Dies ermöglichte es dem „ Java Application Developer“ , Problem 1 zu lösen , indem die Arbeit in Hintergrund-Threads verlagert wurde. Anstatt den Thread während der langen Pause warten zu lassen, kann der Thread jetzt verwendet werden, um andere Anforderungen zu verarbeiten.

FAZIT:

Async-IO in Java verwendet grundsätzlich die nicht blockierende Version der OS-Socket-API.

Die asynchrone Anforderungsverarbeitung ist im Grunde die Standardisierung der Servlet-Spezifikation für die Verarbeitung mehrerer Anforderungen mit einem Thread.

VERWEISE:

//www.scottklement.com/rpg/socktut/tutorial.pdf

//stackoverflow.com/questions/15217524/what-is-the-difference-between-thread-per-connection-vs-thread-per-request

Motivation des Artikels: Team Learning / Wissensaustausch