Sprievodca DeferredResult na jar

1. Prehľad

V tomto návode sa pozrieme na ako môžeme použiť DeferredResult triedy na jar MVC vykonať asynchrónne spracovanie žiadosti.

Asynchrónna podpora bola predstavená v servlete 3.0 a zjednodušene povedané umožňuje spracovanie požiadavky HTTP v inom vlákne ako vlákno prijímača požiadavky.

DeferredResult, dostupný od jari 3.2 a neskôr, pomáha pri odľahčení dlhotrvajúceho výpočtu z vlákna http-worker do samostatného vlákna.

Aj keď druhé vlákno zaberie nejaké prostriedky na výpočet, pracovné vlákna nie sú medzitým blokované a dokážu spracovať prichádzajúce požiadavky klientov.

Model spracovania asynchrónnych požiadaviek je veľmi užitočný, pretože pomáha dobre škálovať aplikáciu pri vysokom zaťažení, najmä pri operáciách intenzívnych IO.

2. Inštalácia

Pre naše príklady použijeme aplikáciu Spring Boot. Viac podrobností o zavedení aplikácie nájdete v našom predchádzajúcom článku.

Ďalej si ukážeme synchrónnu aj asynchrónnu komunikáciu pomocou DeferredResult a tiež porovnajte, ako je asynchrónne lepšie škálovateľné pre prípady vysokého zaťaženia a IO náročné použitie.

3. Blokovanie služby REST

Začnime vývojom štandardnej blokujúcej služby REST:

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

Tu je problém vlákno na spracovanie žiadosti je blokované, kým sa nespracuje úplná žiadosť a výsledok sa vráti. V prípade dlhodobých výpočtov ide o suboptimálne riešenie.

Aby sme to vyriešili, môžeme lepšie využívať vlákna kontajnera na vybavovanie požiadaviek klientov, ako uvidíme v nasledujúcej časti.

4. Neblokovanie REST pomocou DeferredResult

Aby sme sa vyhli blokovaniu, použijeme programovací model založený na spätných volaniach, kde namiesto skutočného výsledku vrátime a DeferredResult do servletovej nádoby.

@GetMapping ("/ async-deferredresult") verejné DeferredResult handleReqDefResult (model model) {LOG.info ("Prijatá požiadavka async-deferredresult"); DeferredResult výstup = nový DeferredResult (); ForkJoinPool.commonPool (). Submit (() -> {LOG.info ("Spracovanie v samostatnom vlákne"); vyskúšať {Thread.sleep (6000);} catch (InterruptedException e) {} output.setResult (ResponseEntity.ok ( „ok“)));}); LOG.info ("vlákno servletu uvoľnené"); spätný výstup; }

Spracovanie žiadosti sa vykonáva v samostatnom vlákne a po dokončení vyvoláme setResult prevádzka na DeferredResult objekt.

Pozrime sa na výstup protokolu a skontrolujte, či sa naše vlákna správajú podľa očakávania:

[nio-8080-exec-6] com.baeldung.controller.AsyncDeferredResultController: Prijatá požiadavka async-deferredresult [nio-8080-exec-6] com.baeldung.controller.AsyncDeferredResultController: vlákno servletu uvoľnené [nio-8080-exec-6 ] java.lang.Thread: Spracovanie v samostatnom vlákne

Interne je informované vlákno kontajnera a klientovi je doručená odpoveď HTTP. Spojenie zostane otvorené v kontajneri (servlet 3.0 alebo novší), kým nepríde odpoveď alebo nevyprší čas.

5. DeferredResult Spätné volania

Môžeme zaregistrovať 3 typy spätných volaní s DeferredResult: dokončenie, časový limit a chybové spätné volania.

Použime pri dokončovaní () metóda na definovanie bloku kódu, ktorý sa vykoná po dokončení asynchronnej požiadavky:

deferredResult.onCompletion (() -> LOG.info ("Spracovanie je dokončené"));

Podobne môžeme použiť onTimeout () zaregistrovať vlastný kód na vyvolanie, ak dôjde k vypršaniu časového limitu. Aby sme obmedzili čas spracovania žiadosti, môžeme počas DeferredResult vytváranie objektov:

DeferredResult deferredResult = nový DeferredResult (500 l); deferredResult.onTimeout (() -> deferredResult.setErrorResult (ResponseEntity.status (HttpStatus.REQUEST_TIMEOUT) .body ("nastal časový limit požiadavky.")));

V prípade vypršania časového limitu nastavujeme iný stav odozvy prostredníctvom obslužného programu vypršania časového limitu zaregistrovaného u DeferredResult.

Spustíme chybu časového limitu spracovaním žiadosti, ktorá trvá dlhšie ako definované hodnoty časového limitu 5 sekúnd:

ForkJoinPool.commonPool (). Submit (() -> {LOG.info ("Spracovanie v samostatnom vlákne"); vyskúšať {Thread.sleep (6000);} catch (InterruptedException e) {...} deferredResult.setResult (ResponseEntity .dobre dobre"))); });

Pozrime sa na protokoly:

[nio-8080-exec-6] com.baeldung.controller.DeferredResultController: vlákno servletu uvoľnené [nio-8080-exec-6] java.lang.Thread: Spracovanie v samostatnom vlákne [nio-8080-exec-6] com. baeldung.controller.DeferredResultController: nastal časový limit požiadavky

Budú existovať scenáre, kedy dlhotrvajúci výpočet zlyhá z dôvodu chyby alebo výnimky. V takom prípade môžeme zaregistrovať aj onError () zavolaj späť:

deferredResult.onError ((Throwable t) -> {deferredResult.setErrorResult (ResponseEntity.status (HttpStatus.INTERNAL_SERVER_ERROR) .body ("Vyskytla sa chyba."));});

V prípade chyby pri výpočte odpovede nastavujeme prostredníctvom tohto obslužného programu chyby iný stav odpovede a telo správy.

6. Záver

V tomto rýchlom článku sme sa pozreli na to, ako Spring MVC DeferredResult uľahčuje vytváranie asynchrónnych koncových bodov.

Celý zdrojový kód je ako obvykle k dispozícii na serveri Github.


$config[zx-auto] not found$config[zx-overlay] not found