Gids voor uitgesteld resultaat in het voorjaar

1. Overzicht

In deze tutorial zullen we kijken naar hoe we de Uitgesteld Resultaat class in Spring MVC om asynchrone aanvraagverwerking uit te voeren.

Asynchrone ondersteuning werd geïntroduceerd in Servlet 3.0 en, simpel gezegd, het maakt het mogelijk om een ​​HTTP-verzoek in een andere thread te verwerken dan de thread van de verzoekontvanger.

Uitgesteld resultaat, beschikbaar vanaf Spring 3.2, helpt bij het overzetten van een langlopende berekening van een http-worker-thread naar een aparte thread.

Hoewel de andere thread enige bronnen nodig heeft voor de berekening, worden de werkthreads in de tussentijd niet geblokkeerd en kunnen ze inkomende clientverzoeken afhandelen.

Het asynchrone verzoekverwerkingsmodel is erg handig omdat het helpt om een ​​applicatie goed te schalen tijdens hoge belasting, vooral voor IO-intensieve bewerkingen.

2. Installatie

Voor onze voorbeelden gebruiken we een Spring Boot-applicatie. Raadpleeg ons vorige artikel voor meer informatie over het opstarten van de applicatie.

Vervolgens demonstreren we zowel synchrone als asynchrone communicatie met behulp van Uitgesteld Resultaat en vergelijk ook hoe asynchrone een beter schaalt voor gevallen van hoge belasting en IO-intensief gebruik.

3. REST-service blokkeren

Laten we beginnen met het ontwikkelen van een standaard blokkerende REST-service:

@GetMapping ("/ process-blocking") openbare ResponseEntity handleReqSync (Modelmodel) {// ... return ResponseEntity.ok ("ok"); }

Het probleem hier is dat de thread voor het verwerken van verzoeken wordt geblokkeerd totdat het volledige verzoek is verwerkt en het resultaat wordt geretourneerd. Bij langlopende berekeningen is dit een suboptimale oplossing.

Om dit aan te pakken, kunnen we beter gebruik maken van container-threads om klantverzoeken af ​​te handelen, zoals we in de volgende sectie zullen zien.

4. Niet-blokkerende REST gebruiken Uitgesteld Resultaat

Om blokkering te voorkomen, gebruiken we een op callbacks gebaseerd programmeermodel, waarbij we in plaats van het daadwerkelijke resultaat een Uitgesteld Resultaat naar de servlet-container.

@GetMapping ("/ async-deferredresult") openbaar DeferredResult handleReqDefResult (Modelmodel) {LOG.info ("Async-uitgesteld resultaat verzoek ontvangen"); Uitgesteld Resultaat output = nieuwe DeferredResult (); ForkJoinPool.commonPool (). Submit (() -> {LOG.info ("Verwerking in aparte thread"); probeer {Thread.sleep (6000);} catch (InterruptedException e) {} output.setResult (ResponseEntity.ok ( "OK")); }); LOG.info ("servlet thread bevrijd"); terugkeer output; }

Verzoekverwerking gebeurt in een aparte thread en zodra deze is voltooid, roepen we de setResult operatie op de Uitgesteld Resultaat voorwerp.

Laten we eens kijken naar de loguitvoer om te controleren of onze threads zich gedragen zoals verwacht:

[nio-8080-exec-6] com.baeldung.controller.AsyncDeferredResultController: async-uitgesteld resultaat verzoek ontvangen [nio-8080-exec-6] com.baeldung.controller.AsyncDeferredResultController: Servlet-thread vrijgemaakt [nio-8080-exec-6 ] java.lang.Thread: Verwerking in aparte thread

Intern wordt de containerthread op de hoogte gesteld en wordt het HTTP-antwoord aan de client geleverd. De verbinding blijft open door de container (servlet 3.0 of hoger) totdat het antwoord arriveert of een time-out optreedt.

5. Uitgesteld Resultaat Terugbellen

We kunnen 3 soorten callbacks registreren met een DeferredResult: voltooiing, time-out en fout callbacks.

Laten we de ter afronding() methode om een ​​codeblok te definiëren dat wordt uitgevoerd wanneer een asynchroon verzoek is voltooid:

deferredResult.onCompletion (() -> LOG.info ("Verwerking voltooid"));

Evenzo kunnen we gebruiken onTimeout () om aangepaste code te registreren die moet worden aangeroepen zodra een time-out optreedt. Om de verwerkingstijd van verzoeken te beperken, kunnen we een time-outwaarde doorgeven tijdens de Uitgesteld Resultaat object creatie:

Uitgesteld Resultaat deferredResult = nieuw DeferredResult (500l); deferredResult.onTimeout (() -> deferredResult.setErrorResult (ResponseEntity.status (HttpStatus.REQUEST_TIMEOUT) .body ("Request time-out opgetreden.")));

In het geval van time-outs stellen we een andere antwoordstatus in via de time-outhandler geregistreerd bij Uitgesteld Resultaat.

Laten we een time-outfout activeren door een verzoek te verwerken dat langer duurt dan de gedefinieerde time-outwaarden van 5 seconden:

ForkJoinPool.commonPool (). Submit (() -> {LOG.info ("Verwerking in aparte thread"); probeer {Thread.sleep (6000);} catch (InterruptedException e) {...} deferredResult.setResult (ResponseEntity .oke oke"))); });

Laten we naar de logboeken kijken:

[nio-8080-exec-6] com.baeldung.controller.DeferredResultController: servlet thread bevrijd [nio-8080-exec-6] java.lang.Thread: verwerking in aparte thread [nio-8080-exec-6] com. baeldung.controller.DeferredResultController: Time-out van verzoek opgetreden

Er zullen scenario's zijn waarin langlopende berekeningen mislukken vanwege een fout of uitzondering. In dit geval kunnen we ook een onError () Bel terug:

deferredResult.onError ((Throwable t) -> {deferredResult.setErrorResult (ResponseEntity.status (HttpStatus.INTERNAL_SERVER_ERROR) .body ("Er is een fout opgetreden."));});

In het geval van een fout stellen we tijdens het berekenen van het antwoord een andere antwoordstatus en berichttekst in via deze foutafhandelaar.

6. Conclusie

In dit korte artikel hebben we gekeken naar hoe Spring MVC Uitgesteld Resultaat vereenvoudigt het maken van asynchrone eindpunten.

Zoals gewoonlijk is de volledige broncode beschikbaar op Github.