Spring MVC Streaming und SSE Request Processing
1. Einführung
In diesem einfachen Lernprogramm wird die Verwendung mehrerer asynchroner und Streaming-Objekte in Spring MVC 5.x.x veranschaulicht.
Im Einzelnen werden drei Schlüsselklassen besprochen:
-
ResponseBodyEmitter
-
SseEmitter
-
StreamingResponseBody
Außerdem werden wir diskutieren, wie Sie mit ihnen über einen JavaScript-Client interagieren können.
2. ResponseBodyEmitter
ResponseBodyEmitter verarbeitet asynchrone Antworten.
Außerdem stellt es ein übergeordnetes Element für eine Reihe von Unterklassen dar. Eine davon wird im Folgenden näher erläutert.
2.1. Serverseite
Es ist besser, einResponseBodyEmitter zusammen mit einem eigenen dedizierten asynchronen Thread zu verwenden und mit einemResponseEntity zu umschließen (in das wir dieemitter direkt einfügen können):
@Controller
public class ResponseBodyEmitterController {
private ExecutorService executor
= Executors.newCachedThreadPool();
@GetMapping("/rbe")
public ResponseEntity handleRbe() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
executor(() -> {
try {
emitter.send(
"/rbe" + " @ " + new Date(), MediaType.TEXT_PLAIN);
emitter.complete();
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
return new ResponseEntity(emitter, HttpStatus.OK);
}
}
Im obigen Beispiel alsowe can sidestep needing to use CompleteableFutures, kompliziertere asynchrone Versprechen oder Verwendung der Annotation@Async.
Stattdessen deklarieren wir einfach unsere asynchrone Entität und verpacken sie in ein neuesThread, das vonExecutorService. bereitgestellt wird
2.2. Client-Seite
Für die clientseitige Verwendung können wir eine einfache XHR-Methode verwenden und unsere API-Endpunkte wie bei einer normalen AJAX-Operation aufrufen:
var xhr = function(url) {
return new Promise(function(resolve, reject) {
var xmhr = new XMLHttpRequest();
//...
xmhr.open("GET", url, true);
xmhr.send();
//...
});
};
xhr('http://localhost:8080/javamvcasync/rbe')
.then(function(success){ //... });
3. SseEmitter
SseEmitter is actually a subclass of ResponseBodyEmitter and provides additional Server-Sent Event (SSE) sofort einsatzbereite Unterstützung.
3.1. Serverseite
Werfen wir also einen kurzen Blick auf einen Beispiel-Controller, der diese leistungsstarke Entität nutzt:
@Controller
public class SseEmitterController {
private ExecutorService nonBlockingService = Executors
.newCachedThreadPool();
@GetMapping("/sse")
public SseEmitter handleSse() {
SseEmitter emitter = new SseEmitter();
nonBlockingService.execute(() -> {
try {
emitter.send("/sse" + " @ " + new Date());
// we could send more events
emitter.complete();
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
return emitter;
}
}
Ziemlich normaler Tarif, aber wir werden einige Unterschiede zwischen diesem und unserem üblichen REST-Controller feststellen:
-
Zuerst geben wir aSseEmitter zurück
-
Außerdem verpacken wir die Kernantwortinformationen in eigeneThread
-
Schließlich senden wir Antwortinformationen mit emitter.send()
3.2. Client-Seite
Unser Client arbeitet diesmal etwas anders, da wir die kontinuierlich verbundeneServer-Sent Event-Bibliothek nutzen können:
var sse = new EventSource('http://localhost:8080/javamvcasync/sse');
sse.onmessage = function (evt) {
var el = document.getElementById('sse');
el.appendChild(document.createTextNode(evt.data));
el.appendChild(document.createElement('br'));
};
4. StreamingResponseBody
Schließlichwe can use StreamingResponseBody to write directly to an OutputStream before passing that written information back to the client using a ResponseEntity.
4.1. Serverseite
@Controller
public class StreamingResponseBodyController {
@GetMapping("/srb")
public ResponseEntity handleRbe() {
StreamingResponseBody stream = out -> {
String msg = "/srb" + " @ " + new Date();
out.write(msg.getBytes());
};
return new ResponseEntity(stream, HttpStatus.OK);
}
}
4.2. Client-Seite
Wie zuvor verwenden wir eine reguläre XHR-Methode, um auf den oben genannten Controller zuzugreifen:
var xhr = function(url) {
return new Promise(function(resolve, reject) {
var xmhr = new XMLHttpRequest();
//...
xmhr.open("GET", url, true);
xmhr.send();
//...
});
};
xhr('http://localhost:8080/javamvcasync/srb')
.then(function(success){ //... });
Schauen wir uns als nächstes einige erfolgreiche Verwendungen dieser Beispiele an.
5. Alles zusammenbringen
Nachdem wir unseren Server erfolgreich kompiliert und unseren Client oben ausgeführt haben (Zugriff auf die angegebenenindex.jsp), sollte in unserem Browser Folgendes angezeigt werden:
Und Folgendes in unserem Terminal:
Wir können die Endpunkte auch direkt anrufen und sie als Streaming-Antworten in unserem Browser anzeigen.
6. Fazit
Während sichFuture undCompleteableFuture als robuste Ergänzungen zu Java und Spring erwiesen haben, stehen uns jetzt mehrere Ressourcen zur Verfügung, um asynchrone Daten und Streaming-Daten für hochkonkurrierende Webanwendungen angemessener zu verarbeiten.
Überprüfen Sie abschließend die vollständigen Codebeispieleover on GitHub.