๐ Purpose
See how Spring DeferredResult can be asynchronous to greatly increase the number of long connections.
DeferredResult provides an alternative to using a Callable for asynchronous request processing. While a Callable is Executed on behalf of the application concurrently, with a DeferredResult the application can produce the result from a thread of its choice.
DeferredResult provides a way to implement asynchronous requests using the Callable interface. Although Callable is executed concurrently on behalf of the application, using a DeferredResult application can get results from the thread in which it is executed.
๐Prepare to use the DeferredResult application
I can’t catch him so faronError()
The event
@Slf4j
@CrossOrigin
@RestController
@SpringBootApplication
public class DeferredresultSpringmvcApplication {
public static void main(String[] args) {
SpringApplication.run(DeferredresultSpringmvcApplication.class, args);
}
private static ExecutorService executors = Executors.newFixedThreadPool(10);
@RequestMapping("/get0")
public DeferredResult<HashMap<String, Object>> getData() {
log.info("Controller called thread: {}",Thread.currentThread().getName());
log.info("Controller starts executing");
// Set the timeout time, in fact, only asynchrony works
DeferredResult<HashMap<String, Object>> result = new DeferredResult<>(1000L);
result.setResult(getMap());
// Returns true if not executed by thread
executors.execute(new Runnable() {
@Override
public void run(a) {
Success is returned if the asynchronous request has expired (it must be asynchronous to take effect)
boolean isok = false;
isok = result.setResult(getMap());
log.info("Did the DeferredResult process succeed: {}", isok);
// The result of processing after success (must be placed in the Runnable interface of the call)
result.setResultHandler(new DeferredResult.DeferredResultHandler() {
@Override
public void handleResult(Object result) {
log.info("DeferredResult result: {}",result.toString()); }}); }});// I have a problem here // I call thread exception all in the timeout
// I can't catch the exception, I don't know where, actually can customize the exception, and then according to the exception type to determine
result.onError((e) -> {
log.info("onError");
});
// The result will be executed no matter what
result.onCompletion(new Runnable() {
@Override
public void run(a) {
log.info("Handling onCompletion"); }});// Only asynchronous results will be processed
result.onTimeout(new Runnable() {
@Override
public void run(a) {
log.info("Processing timeout onTimeout"); }}); log.info("Controller executed complete");
// Remember to return only result, not null
return result;
}
/** * execute * without thread pool@return* /
@RequestMapping("/get1")
public DeferredResult<HashMap<String, Object>> getByNoExecutor(){
log.info("Controller called thread: {}",Thread.currentThread().getName());
log.info("Controller starts executing");
DeferredResult<HashMap<String, Object>> result = new DeferredResult<>(3000L);
result.setResult(getMap());
log.info("Controller executed complete");
return result;
}
/** * execute * with thread pool@return* /
@RequestMapping("/get2")
public DeferredResult<HashMap<String, Object>> getByExecutor(){
log.info("Controller called thread: {}",Thread.currentThread().getName());
log.info("Controller starts executing");
DeferredResult<HashMap<String, Object>> result = new DeferredResult<>(3000L);
executors.execute(new Runnable() {
@Override
public void run(a) { result.setResult(getMap()); }}); log.info("Controller executed complete");
return result;
}
/** * simulates service *@return* /
public HashMap<String, Object> getMap(a){
log.info("Start service execution");
// Simulate service to delay service
HashMap<String, Object> map = new HashMap<>();
try {
Thread.sleep(2000);
// int i = 1/0;
map.put("key",Thread.currentThread().getName());
log.info("Service Service call thread: {}",Thread.currentThread().getName());
} catch (Exception e) {
throw new RuntimeException();
}
log.info("Service completed");
returnmap; }}Copy the code
We looked at the printout and found
1Get1 Execution result2019-10-12 12:44:17.549 INFO 2020 --- [nio-8080-exec-2] C.S.D eferredresultSpringmvcApplication: controller call thread: HTTP - nio -8080-exec-2
2019-10-12 12:44:17.549 INFO 2020 --- [nio-8080-exec-2] C.S.D eferredresultSpringmvcApplication: the controller starts to be enforced2019-10-12 12:44:17.549 INFO 2020 --- [nio-8080-exec-2] C.S.D eferredresultSpringmvcApplication: service began to perform2019-10-12 12:44:19.549 INFO 2020 --- [nio-8080-exec-2Calling thread] C.S.D eferredresultSpringmvcApplication: service service: HTTP - nio -8080-exec-2
2019-10-12 12:44:19.549 INFO 2020 --- [nio-8080-exec-2] C.S.D eferredresultSpringmvcApplication: service is performed2019-10-12 12:44:19.549 INFO 2020 --- [nio-8080-exec-2] C.S.D eferredresultSpringmvcApplication: controller is performed2Get2 Result2019-10-12 12:44:47.993 INFO 2020 --- [nio-8080-exec-4] C.S.D eferredresultSpringmvcApplication: controller call thread: HTTP - nio -8080-exec-4
2019-10-12 12:44:47.993 INFO 2020 --- [nio-8080-exec-4] C.S.D eferredresultSpringmvcApplication: the controller starts to be enforced2019-10-12 12:44:47.994 INFO 2020 --- [nio-8080-exec-4] C.S.D eferredresultSpringmvcApplication: controller is performed2019-10-12 12:44:47.997 INFO 2020 --- [pool-1-thread-1] C.S.D eferredresultSpringmvcApplication: service began to perform2019-10-12 12:44:49.999 INFO 2020 --- [pool-1-thread-1Calling thread] C.S.D eferredresultSpringmvcApplication: service service: the pool -1-thread-1
2019-10-12 12:44:49.999 INFO 2020 --- [pool-1-thread-1] C.S.D eferredresultSpringmvcApplication: service is performedCopy the code
๐ So how do we implement one ourselves?
Simple implementation
/** * Custom simple use *@return* /
@RequestMapping("/get3")
public MyCallable<HashMap<String, Object>> get(){
log.info("Controller called thread: {}",Thread.currentThread().getName());
log.info("Controller starts executing");
MyCallable<HashMap<String, Object>> objectMyCallable = new MyCallable<>();
log.info("Controller executed complete");
return objectMyCallable;
}
/** * simple execution logic *@param <T>
*/
private class MyCallable<T> implements Callable<T> {
@Override
public T call(a) throws Exception {
return(T)getMap(); }}/** * simulates service *@return* /
public HashMap<String, Object> getMap(a){
log.info("Start service execution");
// Simulate service to delay service
HashMap<String, Object> map = new HashMap<>();
try {
Thread.sleep(2000);
// int i = 1/0;
map.put("key",Thread.currentThread().getName());
log.info("Service Service call thread: {}",Thread.currentThread().getName());
} catch (Exception e) {
throw new RuntimeException();
}
log.info("Service completed");
return map;
}
Copy the code
If we look at the result of our implementation, we find that the task thread is executed, and the thread pool is maintained by MyCallable.
2019-10-12 12:42:23.869 INFO 2020 --- [nio-8080-exec-5] C.S.D eferredresultSpringmvcApplication: controller call thread: HTTP - nio -8080-exec-5
2019-10-12 12:42:23.870 INFO 2020 --- [nio-8080-exec-5] C.S.D eferredresultSpringmvcApplication: the controller starts to be enforced2019-10-12 12:42:23.872 INFO 2020 --- [nio-8080-exec-5] C.S.D eferredresultSpringmvcApplication: controller is performed2019-10-12 12:42:23.875 INFO 2020 --- [ task-1] C.S.D eferredresultSpringmvcApplication: service began to perform2019-10-12 12:42:25.875 INFO 2020 --- [ task-1Calling thread] C.S.D eferredresultSpringmvcApplication: service service: task -1
2019-10-12 12:42:25.875 INFO 2020 --- [ task-1] C.S.D eferredresultSpringmvcApplication: service is performedCopy the code
If we use a thread pool, then we can customize our thread pool so that our task thread doesn’t execute complex code and our thread pool does,
/** * Custom flexibility increases code extensibility by adding thread pools *@return* /
@RequestMapping("/get4")
public MyCallable2<HashMap<String, Object>> get2(){
log.info("Controller called thread: {}",Thread.currentThread().getName());
log.info("Controller starts executing");
MyCallable2<HashMap<String, Object>> objectMyCallable = new MyCallable2<>(new Callable<HashMap<String, Object>>() {
@Override
public HashMap<String, Object> call(a) throws Exception {
returngetMap(); }}); log.info("Controller executed complete");
return objectMyCallable;
}
/** * this is a deferred result *@param <T>
*/
private class MyCallable2<T> implements Callable<T> {
private ExecutorService executors = Executors.newFixedThreadPool(10);
private Callable<T> callable;
public MyCallable2(Callable<T> callable) {
this.callable = callable;
}
// Let a new thread execute the callable method
@Override
public T call(a) throws Exception {
Future<T> submit = executors.submit(callable);
returnsubmit.get(); }}/** * simulates service *@return* /
public HashMap<String, Object> getMap(a){
log.info("Start service execution");
// Simulate service to delay service
HashMap<String, Object> map = new HashMap<>();
try {
Thread.sleep(2000);
// I did not catch the exception
// int i = 1/0;
map.put("key",Thread.currentThread().getName());
log.info("Service Service call thread: {}",Thread.currentThread().getName());
} catch (Exception e) {
throw new RuntimeException();
}
log.info("Service completed");
return map;
}
Copy the code
The execution result
2019-10-12 12:42:44.879 INFO 2020 --- [nio-8080-exec-8] C.S.D eferredresultSpringmvcApplication: controller call thread: HTTP - nio -8080-exec-8
2019-10-12 12:42:44.880 INFO 2020 --- [nio-8080-exec-8] C.S.D eferredresultSpringmvcApplication: the controller starts to be enforced2019-10-12 12:42:44.882 INFO 2020 --- [nio-8080-exec-8] C.S.D eferredresultSpringmvcApplication: controller is performed2019-10-12 12:42:44.883 INFO 2020 --- [pool-2-thread-1] C.S.D eferredresultSpringmvcApplication: service began to perform2019-10-12 12:42:46.884 INFO 2020 --- [pool-2-thread-1Calling thread] C.S.D eferredresultSpringmvcApplication: service service: the pool -2-thread-1
2019-10-12 12:42:46.884 INFO 2020 --- [pool-2-thread-1] C.S.D eferredresultSpringmvcApplication: service is performedCopy the code
๐ Summary
I feel that I have written very detailed, and I won’t go into any more details, but in fact its application scenario is when a processing request takes a long time, you need to use this, the main is to return a processing code logic thread, not return a result, this point should be understood. Because thread A doesn’t wait for thread B to finish before it gets its result, if thread A has to get the result returned by thread B, then thread A must block. Deferedresult is a lot like deferedResult in that it doesn’t wait for the socket listening on port 80 to complete and then returns your handler thread to you if you maintain a thread pool. Otherwise, the socket thread will execute.