https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-async
Web on Servlet Stack
Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, "Spring Web MVC," comes from the name of its source module (spring-webmvc), but it is more commonl
docs.spring.io
틀린 해석이나 내용이 있다면 알려주세요, 감사합니다🤗
- DeferredResult and Callable return values in controller methods provide basic support for a single asynchronous return value.
- Controllers can stream multiple values, including SSE and raw data.
- Controllers can use reactive clients and return reactive types for response handling.
1.6.1.DeferredResult
Servlet Container 에서 asynchronous request 기능을 활성화시키면 Controller Method 반환 값을 모두 DefferedResult 로 래핑할 수 있다
@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Save the deferredResult somewhere..
return deferredResult;
}
// From some other thread...
deferredResult.setResult(result);
외부 이벤트(JMS message), 예약작업 또는 기타 이벤트에 대한 응답으로 스레드에서 반환 값을 비동기적으로 만들 수 있다
1.6.2.Callable
java.util.concurrent.Callable 에서 반환 값을 매핑할 수 있다
@PostMapping
public Callable<String> processUpload(final MultipartFile file) {
return () -> "someView";
}
The return value can then be obtained by running the given task through the configured TaskExecutor.
1.6.3. Processing
overview of Servlet asynchronous request processing
- A ServletRequest can be put in asynchronous mode by calling request.startAsync(). The main effect of doing so is that the Servlet (as well as any filters) can exit, but the response remains open to let processing complete later.
- The call to request.startAsync() returns AsyncContext, which you can use for further control over asynchronous processing. For example, it provides the dispatch method, which is similar to a forward from the Servlet API, except that it lets an application resume request processing on a Servlet container thread.
- The ServletRequest provides access to the current DispatcherType, which you can use to distinguish between processing the initial request, an asynchronous dispatch, a forward, and other dispatcher types.
ServletRequest 를 비동기 모드로 전환하려면 request.startAsync()를 호출한다.
request.startAsync() 에 대한 호출은 AsyncContext를 반환한다(비동기 처리에 대한 추가 제어 가능). 예를들어 Application이 서블릿 컨테이너 스레드에서 요청처리를 다시 할 수 있다는 점을 제외하고 Servlet API 전달과 유사한 Dispatch Method를 제공한다
ServletResponse는 초기 요청 처리, 비동기 디스패치, 전달 및 기타 디스패쳐 유형을 구분하는데 사용할 수 있는 DispatcherType에 대한 액세스를 제공한다
DeferredResult processing works
- The controller returns a DeferredResult and saves it in some in-memory queue or list where it can be accessed.
- Spring MVC calls request.startAsync().
- Meanwhile, the DispatcherServlet and all configured filters exit the request processing thread, but the response remains open.
- The application sets the DeferredResult from some thread, and Spring MVC dispatches the request back to the Servlet container.
- The DispatcherServlet is invoked again, and processing resumes with the asynchronously produced return value.
컨트롤러는 DefferedResult를 반환하고 액세스할 수 있는 메모리 내 대기열 또는 목록에 저장한다
SpringMVC는 request.startAsync() 를 호출한다
DispatcherServlet 및 구성 filter 는 요청 처리 스레드를 종료하지만 응답을 열어둔 채로 유지한다
애플리케이션은 일부 스레드에서 DeferredResult설정하고, Spring MVC는 요청을 Servleet Container 로 다시 dispatch한다
DispatcherServlet 이 다시 호출되고 비동기적으로 생성된 반환 값으로 처리가 재개된다
Callable processing
- The controller returns a Callable.
- Spring MVC calls request.startAsync() and submits the Callable to a TaskExecutor for processing in a separate thread.
- Meanwhile, the DispatcherServlet and all filters exit the Servlet container thread, but the response remains open.
- Eventually the Callable produces a result, and Spring MVC dispatches the request back to the Servlet container to complete processing.
- The DispatcherServlet is invoked again, and processing resumes with the asynchronously produced return value from the Callable.
Spring | Home
Cloud Your code, any cloud—we’ve got you covered. Connect and scale your services, whatever your platform.
spring.io
Exception Handling
DeferredResult를 사용하는 경우 setResult 또는 setErrorResult를 예외와 함께 호출할지 선택할 수 있다.
두 경우 모두 Spring MVC는 처리를 완료하기 위해 요청을 Servlet 컨테이너로 다시 보낸다.
그 후 컨트롤러 메서드가 주어진 값을 반환하거나 주어진 예외를 생성한 것처럼 처리된다.
예외는 일반 예외 처리 메커니즘(예: @ExceptionHandler 메서드 호출)을 거치게 된다.
Callable을 사용하면 비슷한 processing logic이 발생하며 주요 차이점은 결과가 Callable에서 반환되거나 예외가 발생한다.
Async Spring MVC compared to WebFlux
Servlet API는 Filter-Servlet chain 을 통해 하나의 pass를 만들기 위해 구축되었다
비동기 요청 처리를 통해 Filter-Servlet 체인을 종료하지만 추가 처리를 위해 응답을 열어둔다
Spring MVC의 비동기 지원 또한 해당 메커니즘을 중심으로 만들어졌다
Controller 가 반환하면 Filter-Servlet chain이 종료되고 Servlet container thread도 해제된다
나중에 DeferredResult가 설정되면 컨트롤러가 다시 매핑되는 동안에 ASYNC 디스패치(동일 URL) 가 수행되지만 DeferredResult값을 호출하는게 아니라 DeferredResult 값을 사용하여 (반환 한것 처럼) 처리를 한다
대조적으로 Spring WebFlux는 ServletAPI 기반 구축이 아니며, 비동기식 자체로 설계되었기 때문에 비동기 요청 처리가 필요하지않다
비동기 처리가 모든 프레임워크에 내장되어있다. 프로그래밍 모델 관점에서 Spring MVC와 Spring WebFlux는 Controller method 반환 값으로 비동기 및 반응형 유형을 지원한다
Spring MVC는 reactive back pressure 를 포함하여 스트리밍 또한 지원한다.
그러나 응담에 대한 개별 writes는 차단되지않은 I/O에 의존하고, 각 writers에 추가 thread가 필요하지않은 WebFlux와 달리 차단 상태로 유지되어 별도의 thread에서 수행된다
또 다른 차이점은 Spring MVC가 컨트롤러 메서드 arguments(@RequestBody, @RequestPart and others)에서 비동기 또는 반응유형을 지원하지않으며, model attribute로도 명시적 지원하지않는다. Spring WebFlux는 지원한다
마지막으로 구성관점에서 비동기 요청 처리 기능은 서블릿 컨테이너 수준에서 활성화되어야 한다
+ Spring WebFlux 는 비동기적 none-blocking 방식이라서 Node.js 처럼 이벤트 루트가 돌고, 요청이 발생하면 그에 맞는 핸들러에처리를 위임 후 처리 완료 시 callback 한다
1.6.4. HTTP Streaming
Single 비동기 반환 값에 대해 DeferredResult 및 Callable 을 사용할 수 있다
여러 비동기 값을 생성하고 이에 대한 응담을 기록하는 방법이다
Objects
You can use the ResponseBodyEmitter return value to produce a stream of objects, where each object is serialized with an HttpMessageConverter and written to the response.
@GetMapping("/events")
public ResponseBodyEmitter handle() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
// Save the emitter somewhere..
return emitter;
}
// In some other thread
emitter.send("Hello once");
// and again later on
emitter.send("Hello again");
// and done at some point
emitter.complete();
ResponseBodyEmitter에서 ResponseEntity 응답상태와 헤더를 지정할 수 있다
When an emitter throws an IOException (for example, if the remote client went away), applications are not responsible for cleaning up the connection and should not invoke emitter.complete or emitter.completeWithError. Instead, the servlet container automatically initiates an AsyncListener error notification, in which Spring MVC makes a completeWithError call. This call, in turn, performs one final ASYNC dispatch to the application, during which Spring MVC invokes the configured exception resolvers and completes the request.
SSE
SseEmitter (a subclass of ResponseBodyEmitter) provides support for Server-Sent Events, where events sent from the server are formatted according to the W3C SSE specification. To produce an SSE stream from a controller, return SseEmitter
@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handle() {
SseEmitter emitter = new SseEmitter();
// Save the emitter somewhere..
return emitter;
}
// In some other thread
emitter.send("Hello once");
// and again later on
emitter.send("Hello again");
// and done at some point
emitter.complete();
While SSE is the main option for streaming into browsers, note that Internet Explorer does not support Server-Sent Events.
Consider using Spring’s WebSocket messaging with SockJS fallback transports (including SSE) that target a wide range of browsers.
Raw Data
Sometimes, it is useful to bypass message conversion and stream directly to the response OutputStream (for example, for a file download). You can use the StreamingResponseBody return value type to do so,
@GetMapping("/download")
public StreamingResponseBody handle() {
return new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// write...
}
};
}
StreamingResponseBody에서 ResponseEntity 응답의 상태 및 헤더를 정의할 수 있다
1.6.5. Reactive Types
Spring MVC는 컨트롤러에서 반응형 Client library 사용을 지원한다
여기에는 Spring-webflux 의 WebClient 및 Spring Data reactive data repositories와 같은 기타 항목이 포함된다
컨트롤러 메서드에서 반응형을 반환할 수 있어 편리하다
- A single-value promise is adapted to, similar to using DeferredResult. Examples include Mono (Reactor) or Single (RxJava).
- A multi-value stream with a streaming media type (such as application/x-ndjson or text/event-stream) is adapted to, similar to using ResponseBodyEmitter or SseEmitter. Examples include Flux (Reactor) or Observable (RxJava). Applications can also return Flux<ServerSentEvent> or Observable<ServerSentEvent>.
- A multi-value stream with any other media type (such as application/json) is adapted to, similar to using DeferredResult<List<?>>.
Spring MVC supports Reactor and RxJava through the ReactiveAdapterRegistry from spring-core, which lets it adapt from multiple reactive libraries.
응답에 대한 write는 여전히 차단되어있기 때문에, TaskExecutor로 구성해야한다.
For streaming to the response, reactive back pressure is supported, but writes to the response are still blocking and are run on a separate thread through the configured TaskExecutor, to avoid blocking the upstream source (such as a Flux returned from WebClient). By default, SimpleAsyncTaskExecutor is used for the blocking writes, but that is not suitable under load. If you plan to stream with a reactive type, you should use the MVC configuration to configure a task executor.
Web on Servlet Stack
Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, "Spring Web MVC," comes from the name of its source module (spring-webmvc), but it is more commonl
docs.spring.io
1.6.6. Context Propagation
Context Propagation은 java.lang.ThreadLocal 를 통해 하는 것이 일반적이다. 이는 동일한 스레드에서 처리하는 경우 작동하지만 여러 스레드에서 비동기식으로 처리하려면 추가 작업이 필요하다.
The Micrometer Context Propagation library simplifies context propagation across threads, and across context mechanisms such as ThreadLocal values, Reactor context, GraphQL Java context, and others.
클래스 경로에 Micrometer Context Propagation이 있는 경우 컨트롤러 method가 Flux또는 Mono와 같은 반응형을 반환할 때 Reactor Context에 key-value 값으로 기록된다
ThreadLocalAcccessor에서 할당한 키로 쌍을 이룬다
다른 비동기 처리 시나리의 경우는 context propagation library를 직접 사용할 수 있다
// Capture ThreadLocal values from the main thread ...
ContextSnapshot snapshot = ContextSnapshot.captureAll();
// On a different thread: restore ThreadLocal values
try (ContextSnapshot.Scope scoped = snapshot.setThreadLocals()) {
// ...
}
https://micrometer.io/docs/contextPropagation
1.6.7. Disconnects
Servlet API는 원격 클라이언트가 사라질 때 알리지않는다. 따라서 SSEEmitter, reactive types 이든 클라이언트의 연결이 끊어지면 쓰기가 실패하므로 주기적으로 데이터를 보내야한다. 전송은 비어있는 애노테이션 전용 SSE 이벤트나 상대와 heart beat 를 해석하고 무시하는 데이터의 형태를 취할 수 있다.
heartbeat 메커니즘이 내정된 웹 메세징 솔루션(STOMP over WebSocket, WebSocket with SockJS) 등을 사용하면 된다
1.6.8. Configuration
비동기 요청 처리 기능은 Servlet Container 수준에서 활성화 되어야한다
MVC 구성은 또한 비동기 요청에 대한 옵션을 제공한다
Servlet Container
Filter and Servlet 선언에서 비동기 요청 처리를 활성화 하려면 syncSupported flag를 true로 설정한다. 또한 ASYNC jakarta.servlet.DispatchType을 처리하도록 필터 매핑을 선언해야한다
JAVA에서는 AbstractAnnotationConfigDispatcherServletInitializer로 Servlet 컨테이너를 초기화하면 자동으로 수행된다
XML에서는 DispatcherServlet 및 필터 선언에 <async-supported>true</async-supported>를 추가하고 필터 매핑에 <dispatcher>ASYNC</dispatcher>를 추가하면 된다
Spring MVC
MVC 구성에서의 비동기 요청 처리 옵션
The MVC configuration exposes the following options related to asynchronous request processing:
- Java configuration: Use the configureAsyncSupport callback on WebMvcConfigurer.
- XML namespace: Use the <async-support> element under <mvc:annotation-driven>.
- 설정되지 않은 경우 default 서블릿 컨테이너에 따라 비동기 요청 시간은 기본 제한 시간 값이다
- AsyncTaskExecutor : Reactive Types로 스트리밍 시 쓰기를 차단하고 Controller method에서 반환된 Callable 인스턴스를 실행하는데 사용한다 기본적으로 SimpleAsyncTaskExecutor이기 때문에 반응형 유형으로 스트리밍하거나 Callable을 반환할 경우 해당 속성을 구성하는게 좋다
- DeferredResultProcessingInterceptor implementations and CallableProcessingInterceptor implementations.
DeferredResult, ResponseBodyEmitter 및 SseEmitter에서 기본 제한 시간 값을 설정할 수도 있다
Callable 의 경우 WebAsyncTask를 사용하여 제한 시간 값을 설정할 수 있다
'Web > spring' 카테고리의 다른 글
[Spring framework Core] 5. Aspect Oriented Programming with Spring (2) (0) | 2023.02.17 |
---|---|
[Spring framework Core] 5. Aspect Oriented Programming with Spring (1) (0) | 2023.02.16 |
[Spring framework testing] 5. Spring TestContext Framework (4) (0) | 2023.02.14 |
[Spring framework Web MVC docs] 1.7. CORS (0) | 2023.02.14 |
[Spring framework testing] 5. Spring TestContext Framework (3) (0) | 2023.02.13 |