๐Ÿ˜† 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.