Spring MVC бо коркарди асинхронии дархостҳо дар Servlet 3.0 васеъ муттаҳид шудааст:
-
Арзиши бозгашти
DeferredResult
ваCallable
дар методҳои контроллер барои дастгирии як арзиши асинхронии бозгаштии асосӣ пешниҳод карда мешавад. -
Контроллерҳо метавонанд чандин арзишро пайдарпай фиристанд, аз ҷумла SSE ва маълумоти хом.
-
Контроллерҳо метавонанд муштариёни реактивиро истифода баранд ва намуди реактивиро барои коркарди ҷавобҳо бозгардонанд.
DeferredResult
Вақте ки функсияи коркарди асинхронии дархостҳо дар контейнери сервлетҳо фаъол мешавад, методҳои контроллер метавонанд ҳар як арзиши бозгашти методи контроллерро бо истифодаи DeferredResult
пӯшанд, чунон ки дар мисоли зерин нишон дода шудааст:
@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Аз ягон ҷараёни дигар...
return deferredResult;
}
// deferredResult-ро ягон ҷо нигоҳ медорем...
deferredResult.setResult(result);
@GetMapping("/quotes")
@ResponseBody
fun quotes(): DeferredResult<String> {
val deferredResult = DeferredResult<String>()
// Аз ягон ҷараёни дигар...
return deferredResult
}
// deferredResult-ро ягон ҷо нигоҳ медорем...
deferredResult.setResult(result)
Контроллер метавонад арзиши бозгаштро асинхронӣ, аз ҷараёни дигар гирифта – масалан, дар ҷавоб ба ҳодисаи берунӣ (пахши JMS), вазифаи муқарраршуда ё ҳодисаи дигар.
Callable
Контроллер метавонад ҳар як арзиши бозгаштиро бо истифодаи java.util.concurrent.Callable
пӯшад, чунон ки дар мисоли зерин нишон дода шудааст:
@PostMapping
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public String call() throws Exception {
// ...
return "someView";
}
};
}
@PostMapping
fun processUpload(file: MultipartFile) = Callable<String> {
// ...
"someView"
}
Арзиши бозгаштро метавон бо иҷрои ин вазифа тавассути TaskExecutor
, ки танзим шудааст, ба даст овард.
Коркард
Баъдан, коркарди асинхронии дархостҳои сервлетҳо мухтасар тасвир шудааст:
-
ServletRequest
метавонад ба ҳолати асинхронӣ гузарад, бо даъватиrequest.startAsync()
. Натичаи асосии ин он аст, ки сервлет (ва ҳар гуна филтрҳо) метавонанд иҷроишро анҷом диҳанд, аммо ҷавоб кушода мемонад, то ки коркард дертар анҷом ёбад. -
Даъвати
request.startAsync()
AsyncContext
-ро бозгардонида, ки онро барои назорати бештар бар арзёбии асинхронии коркарди дархост истифода бурдан мумкин аст. Масалан, он методиdispatch
-ро пешбинӣ мекунад, ки ба пешрафти аз Servlet API монанд аст, ба истиснои он, ки ба барнома иҷозат медиҳад, ки коркарди дархостро дар ҷараёни контейнерҳои сервлетҳо идома диҳад. -
ServletRequest
дастрасӣ баDispatcherType
-и ҷории худро пешбинӣ мекунад, ки барои фарқ кардани коркарди дархости ибтидоӣ, паҳнкунии асинхронӣ, пешрафт ва дигар намудҳои диспетчерҳо истифода бурдан мумкин аст.
Коркарди DeferredResult
чунин кор мекунад:
-
Контроллер
DeferredResult
-ро бозгардонида, онро дар ягон навбат ё рӯйҳати дар ёд нигоҳ дошта медонад, ки ба он дастрасӣ пайдо кардан мумкин аст. -
Spring MVC
request.startAsync()
-ро даъват мекунад. -
Дар ҳамин вақт,
DispatcherServlet
ва ҳама филтрҳои танзимшуда аз ҷараёни коркарди дархост мебароянд, аммо ҷавоб кушода мемонад. -
Барнома
DeferredResult
-ро аз ягон ҷараён мегузорад, ва Spring MVC дархостро ба контейнерҳои сервлетҳо равон мекунад. -
DispatcherServlet
барои боз даъват карда мешавад, ва коркард бо истифодаи арзиши асинхронӣ бозгардонида мешавад.
Коркарди Callable
чунин аст:
-
Контроллер
Callable
-ро бозгардонида мекунад. -
Spring MVC
request.startAsync()
-ро даъват карда,Callable
-ро баTaskExecutor
барои коркард дар ҷараёни алоҳида медиҳад. -
Дар ҳамин вақт,
DispatcherServlet
ва ҳама филтрҳои контейнерҳои сервлетҳо аз ҷараён мебароянд, аммо ҷавоб кушода мемонад. -
Дар натиҷа,
Callable
натиҷаро медиҳад, ва Spring MVC дархостро ба контейнерҳои сервлетҳо барои анҷоми коркарди дархост равон мекунад. -
DispatcherServlet
барои боз даъват карда мешавад, ва коркард бо истифодаи арзиши асинхронӣ азCallable
бозгардонида мешавад.
Барои маълумоти иловагӣ ва контекст, инчунин метавонед бо мақолаҳои блог шинос шавед, ки дар онҳо дастгирии асинхронӣ барои Spring MVC 3.2 пешниҳод шудааст.
Коркарди истисноҳо
Агар шумо DeferredResult
-ро истифода баред, метавонед интихоб кунед, ки setResult
ё setErrorResult
бо истисно даъват кунед. Дар ҳар ду ҳолат, Spring MVC дархостро боз ба контейнерҳои сервлетҳо барои анҷоми коркарди дархост равон мекунад. Пас, он ба назар гирифта мешавад, чунон ки контроллер методи пурра баргардонидашуда ё истисноеро, ки ба назар гирифта шуда буд, баргардонида бошад. Пас, истисно аз механизми коркарди истисноҳои муқаррарӣ мегузарад (масалан, даъват кардани методҳои аннотирована @ExceptionHandler
).
Вақте ки Callable
истифода мешавад, мантиқи коркарди монанд амалӣ мешавад, аммо фарқи асосӣ дар он аст, ки арзиши баргардонии аз Callable
мебарояд ё истисно дар он рух медиҳад.
Паҳншавӣ
Намунаҳои HandlerInterceptor
метавонанд намуди AsyncHandlerInterceptor
дошта бошанд, то даъвати бозгардонишударо ба дархости ибтидоӣ таъмин кунанд, ки асинхронӣ коркарди дархостро оғоз мекунад (ба ҷои postHandle ва afterCompletion).
Иҷрои HandlerInterceptor
метавонад CallableProcessingInterceptor
ё DeferredResultProcessingInterceptor
-ро барои ҳамгироии амиқтар бо давраи ҳаёти дархости асинхронӣ бақайд гирад (масалан, барои коркарди ҳодисаи вақтгузарҳо). Барои маълумоти бештар ба AsyncHandlerInterceptor
нигаред.
DeferredResult
даъватҳои бозгардонро onTimeout(Runnable)
ва onCompletion(Runnable)
таъмин мекунад. Маълумоти бештарро дар javadoc оид ба DeferredResult
бинед. Callable
метавонад баWebAsyncTask
иваз шавад, ки методҳои иловагиро барои вақти интизорӣ ва бозгардонӣ таъмин мекунад.
Дар муқоиса бо WebFlux
Servlet API аввалан барои иҷрои як гузариш дар занҷири "филтр-сервлет" тарҳрезӣ шудааст. Коркарди асинхронӣ, ки ба Servlet 3.0 илова шуда буд, ба барномаҳо иҷозат медиҳад, ки аз занҷири "филтр-сервлет" бароранд, аммо ҷавоб кушода мемонад, ки барои коркарди иловагии дертар. Дастгирии асинхронӣ дар Spring MVC аз ин механизми асосӣ сохта шудааст. Вақте ки контроллер DeferredResult
-ро бозгардонад, занҷири "филтр-сервлет" ба анҷом мерасад, ва ҷараёни контейнерҳо озод карда мешаванд. Баъдтар, вақте ки DeferredResult
танзим мешавад, фиристодани ASYNC
ба амал меояд (ба ҳамон URL), ҳангоми ки контроллер боз ҷуфт мешавад, аммо ба ҷои даъвати, арзиши DeferredResult
-ро (чунон ки контроллер онро бозгардонад) барои идомаи коркард истифода мебарад.
Ба муқобили ин, Spring WebFlux дар Servlet API сохта нашудааст ва ба чунин функсияи коркарди асинхронии дархостҳо эҳтиёҷ надорад, зеро асинхронӣ ба таври таърифӣ дастгирӣ мешавад. Коркарди асинхронӣ ба ҳама шартномаҳои фреймворк ворид шудааст ва дар ҳама марҳилаҳои коркарди дархост дастгирӣ мешавад.
Аз нуқтаи назари модели барномасозӣ, ҳам Spring MVC ва ҳам Spring WebFlux намуди асинхронӣ ва реактивӣ барои арзишҳои бозгашт дар методҳои контроллерро дастгирӣ мекунанд. Spring MVC ҳатто дастгирии пайдарпаи маълумотро, аз ҷумла аҳамияти реактивиро дастгирӣ мекунад. Аммо сабтҳои алоҳида дар ҷавоб мемонанд, ки маҳдуд мешаванд (ва дар ҷараёни алоҳида иҷро мешаванд), дар муқоиса бо WebFlux, ки ба ворид-барори номаҳдуд такя мекунад ва ба ҷараёни иловагӣ барои ҳар як сабт ниёз надорад.
Боз як фарқи асосӣ он аст, ки Spring MVC намуди асинхронӣ ё реактивиро дар параметрҳои методҳои контроллер (масалан, @RequestBody
, @RequestPart
ва дигарон), инчунин дастгирии возеҳ барои намуди асинхронӣ ва реактивӣ ҳамчун атрибутҳои моделиро дастгирӣ намекунад. Spring WebFlux ҳамаи инро дастгирӣ мекунад.
Пайдарпаии HTTP
Шумо метавонед DeferredResult
ва Callable
барои як арзиши асинхронӣ бозгардонишатон истифода баред. Чӣ мешавад, агар ба шумо лозим аст, ки якчанд арзишҳои асинхрониро бигиред ва онҳоро дар ҷавоб нависед? Дар ин қисмат тарзи иҷрои ин амал тавзеҳ дода шудааст.
Объектҳо
Шумо метавонед арзиши бозгардони ResponseBodyEmitter
-ро барои эҷоди ҷараёни объектҳо истифода баред, ки ҳар яки онҳо бо истифодаи HttpMessageConverter
сериализатсия мешаванд ва дар ҷавоб навишта мешаванд, чунон ки дар мисоли зерин нишон дода шудааст:
@GetMapping("/events")
public ResponseBodyEmitter handle() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
// Эмиттерро ягон ҷо нигоҳ медорем...
return emitter;
}
// Дар ҷараёни дигар
emitter.send("Hello once");
// ва боз
emitter.send("Hello again");
// ва дар як лаҳза тайёр мешавад
emitter.complete();
@GetMapping("/events")
fun handle() = ResponseBodyEmitter().apply {
// Эмиттерро ягон ҷо нигоҳ медорем...
}
// Дар ҷараёни дигар
emitter.send("Hello once")
// ва боз
emitter.send("Hello again")
// ва дар як лаҳза тайёр мешавад
emitter.complete()
Шумо инчунин метавонед ResponseBodyEmitter
-ро ҳамчун ҷисми дар ResponseEntity
истифода баред, ки ба шумо имконияти танзими статус ва сарлавҳаҳои ҷавобро медиҳад.
Вақте ки emitter
IOException
-ро (масалан, агар муштарии дурдаст нопадид шавад) тавлид мекунад, барномаҳо барои тоза кардани пайваст масъулият надоранд ва набояд бояд emitter.complete
ё emitter.completeWithError
даъват кунанд. Ба ҷои ин, контейнерҳои сервлетҳо автоматӣ огоҳиномаи хатоии AsyncListener
-ро ифтитоҳ мекунанд, ки дар он Spring MVC низ completeWithError
даъват мекунад. Ин даъват, дар навбати худ, фиристодани охирини ASYNC
ба барнома анҷом медиҳад, дар ҳоле ки Spring MVC истиснонависро, ки конфигуратсия шудаанд, даъват мекунад ва дархостро хатм мекунад.
SSE
SseEmitter
(зеркласси ResponseBodyEmitter
) дастгирии Server-Sent Events, агар ҳодисаҳои фиристодаи сервер бо спецификаи W3C SSE мувофиқат кунанд. Барои эҷоди ҷараёни SSE аз контроллер, SseEmitter
-ро бозгардонед, чунон ки дар мисоли зерин нишон дода шудааст:
@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handle() {
SseEmitter emitter = new SseEmitter();
// Эмиттерро ягон ҷо нигоҳ медорем...
return emitter;
}
// Дар ҷараёни дигар
emitter.send("Hello once");
// ва боз
emitter.send("Hello again");
// ва дар як лаҳза тайёр мешавад
emitter.complete();
@GetMapping("/events", produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
fun handle() = SseEmitter().apply {
// Эмиттерро ягон ҷо нигоҳ медорем...
}
// Дар ҷараёни дигар
emitter.send("Hello once")
// ва боз
emitter.send("Hello again")
// ва дар як лаҳза тайёр мешавад
emitter.complete()
Ҳарчанд SSE ва варианти асосӣ барои ҷараёни маълумот ба браузерҳо аст, дар назар доред, ки Internet Explorer ҳодисаҳои фиристодаи сервер (Server-Sent Events) -ро дастгирӣ намекунад. Истифодаи протоколи WebSocket аз Spring бо механизмҳои алтернативи паҳнкунии SockJS (аз ҷумла SSE) -ро, ки барои доираи васеи браузерҳо пешбинӣ шудааст, шояд баррасӣ кунед.
Маълумоти хом
Базӯрт дар баъзе ҳолатҳо ҳал кардан тавассути танзимоти паёмнависӣ гузароӣ кунад ва ҷараёни иттилоотро бевосита ба OutputStream
ҷавоб диҳад (масалан, вақти боркунии файл). Барои интихоб кардан аз намуди бозгаштии StreamingResponseBody
истифода бурдан мумкин аст, чунон ки дар мисоли зерин нишон дода шудааст:
@GetMapping("/download")
public StreamingResponseBody handle() {
return new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// навиштан...
}
};
}
@GetMapping("/download")
fun handle() = StreamingResponseBody {
// навиштан...
}
Шумо метавонед StreamingResponseBody
-ро ҳамчун ҷисми дар ResponseEntity
истифода баред, ки ба шумо имконияти танзими статус ва сарлавҳаҳои ҷавобро медиҳад.
Намуди реактивӣ
Spring MVC истифодаи китобхонаҳои муштариёни реактивиро дар контроллер дастгирӣ мекунад. Аз ҷумла чунин китобхонаҳо WebClient
аз spring-webflux
ва дигарон, монанди репозиторҳои реактивии лоиҳаи Spring Data. Дар чунин сценариҳо қулай аст, ки намуди реактивиро аз методи контроллер бозгардонад.
Арзишҳои бозгашти реактивӣ чунин коркард мешаванд:
-
Промис бо як арзиши танзимшуда дар ҳолати истифодаи
DeferredResult
. Масалан,Mono
(Reactor) ёSingle
(RxJava). -
Ҷараёни бисёр арзишӣ бо навъи ҳуҷҷати таҳвили маълумоти ҷараёнӣ (масалан,
application/x-ndjson
ёtext/event-stream
) танзим мешавад, ба монанди истифодаиResponseBodyEmitter
ёSseEmitter
. Масалан,Flux
(Reactor) ёObservable
(RxJava). Барномаҳо инчунин метавонандFlux<ServerSentEvent>
ёObservable<ServerSentEvent>
баргардонанд. -
Ҷараёни бисёр арзишӣ бо ҳар навъи дигари ҳуҷҷати таҳвили маълумот (масалан,
application/json
) танзим мешавад, ба монанди истифодаиDeferredResult<List<?>>
.
ReactiveAdapterRegistry
аз
spring-core
дастгирӣ мекунад, ки ба он имкон медиҳад, ки ҳангоми гузариш байни якчанд китобхонаҳои реактивӣ танзим шавад.
Барои паҳнкунии иттилоот дар ҷавоб, реактивии бозгардонӣ дастгирӣ мешавад, аммо навиштан дар ҷавоб бо ҳамон манъ карда мешавад ва дар ҷараёни алоҳида тавассути
Flux
, ки аз
WebClient
баргардонида шудааст) пешгирӣ карда шавад. Бо танзимоти барқӣ истифодаи
SimpleAsyncTaskExecutor
аст, ки барои боргузорӣ мутобиқ нест. Агар ният дошта бошед, ки як чарх бо намуди реактивӣ истифодашаванда бошад, пас, танзимоти MVC-ро барои танзими иҷроишгар истифода намоед.
Пайвастшавии гумшуда
Servlet API ягон огоҳиномаи пайвастшавӣ намедиҳад, вақте ки муштарии дурдаст нопадид шавад. Аз ин рӯ, ҳангоми паҳн кардани ҷавоб, хоҳ бо истифодаи SseEmitter
ё намуди реактивӣ, муҳим аст периодикӣ иттилоотро фиристед, зеро навиштан рӯй намедиҳад, агар муштарӣ пайваст нагашта бошадҳамон намешавад. Пахш метавонад шакли фурӯши холӣ (танҳо барои шарҳ) ё ҳама гуна маълумотҳои дигарро дошта бошад, ки тарафи дигар бояд онро ҳамчун паём ва дарк кунад ва онро нодида гирад.
Ғайр аз ин, шумо метавонед қарорҳои мубодилаи паёмҳои вебро истифода баред (масалан, STOMP бар болои WebSocket ё WebSocket бо SockJS), ки ҳамеша механизми интиқоли паём дар дохил доранд.
Танзимот
Функсияи коркарди асинхронии дархостҳо бояд дар сатҳи контейнерҳои сервлетҳо фаъол карда шавад. Таҳвили MVC инчунин якчанд имконоти барои дархостҳои асинхронӣ пешниҳод мекунад.
Контейнерҳои сервлетҳо
Эълонҳои филтрҳо ва сервлетҳо дорои флаги asyncSupported
мебошанд, ки бояд ба true
танзим карда шавад, ки коркарди асинхрониро фаъол мекунад. Ғайр аз ин, харгушҳои филтр бояд эълон карда шаванд, ки барои коркарди ASYNC javax.servlet.DispatchType
.
Дар таҳвили, вақте, ки шумо AbstractAnnotationConfigDispatcherServletInitializer
барои инициализатсияи контейнерҳои сервлетҳо истифода мекунед, ин амал автоматӣ иҷро мешавад.
Дар web.xml
шумо метавонед <async-supported>true</async-supported>
ба DispatcherServlet
ва ба Filter
декларатсияҳо илова кунед ва илова кунед <dispatcher>ASYNC</dispatcher>
ба роҳандозкунии филтрҳо.
Spring MVC
Таҳвили MVC имконоти зеринро, ки ба коркарди асинхронӣ алоқаманданд, пешниҳод мекунад:
-
Таҳвили: Танзимоти
configureAsyncSupport
бароиWebMvcConfigurer
истифода кунед. -
Фазои номҳои XML: Элементи
<async-support>
дар<mvc:annotation-driven>
истифода кунед.
Шумо метавонед параметрҳои зеринро танзим кунед:
-
Арзиши вақти интизории стандартӣ барои дархостҳои асинхронӣ, ки агар танзим шавад, аз дастгоҳи контейнери сервлетҳо вобаста аст.
-
AsyncTaskExecutor
барои истифодабарии навиштани бозгардонӣ ҳангоми пайдарпай бо намуди реактивӣ ва барои иҷрои намунаҳоиCallable
, ки аз методҳои контроллер бозгардонида мешаванд. Шадидан тавсия дода мешавад, ки ин хосиятро танзим кунед, агар шумо бо намуди реактивӣ кор кунед ё агар шумо методҳои контроллерро, киCallable
бозменамоянд, дошта бошед, зеро он аз рӯи танзимSimpleAsyncTaskExecutor
аст. -
Иҷрои DeferredResultProcessingInterceptor
ва иҷроиCallableProcessingInterceptor
.
Назар кунед, ки шумо инчунин метавонед арзиши вақти интизорӣ барои DeferredResult
, ResponseBodyEmitter
ва SseEmitter
танзим кунед. Барои Callable
шумо метавонед WebAsyncTask
истифода баред, ки арзиши вақти интизориро нишон диҳед.
GO TO FULL VERSION