SpringMVC asynchronous processing of 5 examples

  • 2021-08-31 08:09:10
  • OfStack

Some time ago, I studied the principle of diamond, and one of the important knowledge points is the realization of long connection, which uses the asynchronous processing of servlet. The biggest advantage of asynchronous processing is that it can increase concurrency without blocking the current thread. In fact, Spring MVC also supports asynchronous processing. This article records the relevant technical points.

Asynchronous processing demo

If you want to enable asynchronous return, you need to turn on @ EnableAsync. In the following code, DeferredResult is used for asynchronous processing.

After the request comes in, first create an DeferredResult object and set the timeout time to 60 seconds. Then specify the callback of DeferredResult when it completes asynchronously and waits for timeout. Synchronous processing only needs to create asynchronous any and then return DeferredResult. In this way, after Spring MVC processes this request, it will not immediately return response to the client, but will wait for DeferredResult to complete processing. If DeferredResult is not processed within 60 seconds, a timeout is triggered and response is returned to the client.


@RequestMapping(value = "/async/demo")
public DeferredResult<String> async(){
 //  Create  DeferredResult Set the timeout  60s
 DeferredResult<String> deferredResult = new DeferredResult<>((long)60 * 1000);

 String uuid = UUID.randomUUID().toString();
 Runnable callback = () -> manager.remove(deferredResult, uuid);
 //  Set callbacks for completion and timeout 
 deferredResult.onCompletion(callback);
 deferredResult.onTimeout(callback);

 //  Create asynchronous tasks 
 manager.addAsyncTask(deferredResult, uuid);

 //  Synchronous return  DeferredResult
 return deferredResult;
}

For asynchronous tasks, you need to hold DeferredResult objects. At the end of asynchronous processing, you need to manually call DeferredResult. setResult to complete the output. When setResult is called, the data output is written to the client, and then the asynchronous completion event is triggered to execute the callback.


task.getDeferredResult().setResult(ConfigJsonUtils.toJsonString(map));

Asynchronous processing using DeferredResult

The class DeferredResult represents a delayed result. DeferredResult can be used in asynchronous tasks, and other threads can get DeferredResult and set the return data of DeferredResult. Usually, thread pools, queues, etc. can be used to realize asynchronous processing with DeferredResult.

According to the official description, the processing flow of Spring MVC is as follows:

Save DeferredResult returned by controller in memory queue or collection; Spring MVC calls request. startAsync () to turn on asynchronism; DispatcherServlet and all Filter exit the current request thread; The service application sets the return value of DeferredResult in the asynchronous thread, and Spring MVC will send the request again; DispatcherServlet is called again and the return value of DeferredResult is used;

Asynchronous processing using Callable

Asynchronous processing using Callable is similar to DeferredResult. The difference is that Callable is handed over to the TaskExecutor specified by the system for execution.

According to the official description, the processing flow of Spring MVC is as follows:

controller returns Callable; Spring MVC calls request. startAsync (), turns on asynchronism, and submits Callable to a task thread pool; DispatcherServlet and all Filter exit the current request thread; The business application returns a value in an asynchronous thread, and Spring MVC sends the request again; DispatcherServlet is called again and the return value of Callable is used;

@RequestMapping(value = "/async/demo")
public Callable<String> async(){
 Callable<String> callable = () -> String.valueOf(System.currentTimeMillis());
 //  Synchronous return 
 return callable;
}

Asynchronous processing using ListenableFuture

As the return value, ListenableFuture is similar to DeferredResult. Users are also required to handle asynchronous threads by themselves, but timeouts are not supported, and callbacks are completed, so they need to handle them by themselves.


@RequestMapping(value = "/async/demo")
public ListenableFuture<String> async(){
 ListenableFutureTask<String> ListenableFuture= new ListenableFutureTask<>(() -> {
  return String.valueOf(System.currentTimeMillis());
 });
 Executors.newSingleThreadExecutor().submit(ListenableFuture);
 return ListenableFuture;
}

Asynchronous processing using ResponseBodyEmitter

Both DeferredResult and Callable can return only one asynchronous value. If you need to return multiple objects, use ResponseBodyEmitter. Each object returned is processed by HttpMessageConverter and written back to the output stream. If you want to set more return data, such as header, status, etc., you can return ResponseBodyEmitter as the entity data of ResponseEntity.


@RequestMapping("/async/responseBodyEmitter")
public ResponseBodyEmitter responseBodyEmitter(){
 ResponseBodyEmitter responseBodyEmitter=new ResponseBodyEmitter();

 Executors.newSingleThreadExecutor().submit(() -> {
  try {
   responseBodyEmitter.send("demo");
   responseBodyEmitter.send("test");
   responseBodyEmitter.complete();
  } catch (Exception ignore) {}
 });

 return responseBodyEmitter;
}

Asynchronous processing using StreamingResponseBody

If you want to skip the automatic conversion of the return value and write the output stream directly to OutputStream, you can use StreamingResponseBody. It can also be returned as entity data of ResponseEntity.


@RequestMapping("/async/streamingResponseBody")
public StreamingResponseBody streamingResponseBody(){
 StreamingResponseBody streamingResponseBody = outputStream -> {
  Executors.newSingleThreadExecutor().submit(() -> {
   try {
    outputStream.write("<html>streamingResponseBody</html>".getBytes());
   } catch (IOException ignore) {}
  });
 };
 return streamingResponseBody;
}

Comparison of various treatment methods


可返回次数

数据转换

回调

线程池

DeferredResult

1 次

完成、超时

自行处理

Callable

1 次

系统处理

ListenableFuture

1 次

自行处理

ResponseBodyEmitter

多次

自行处理

StreamingResponseBody

多次

自行处理


Related articles: