[SpringBoot] The use and expansion of asynchronous requests


The difference between asynchronous request and asynchronous call

  • The usage scenarios of the two are different. Asynchronous requests are used to solve them 并发请求对服务器造成的压力, 提高对请求的吞吐量and asynchronous calls are used 做一些非主线流程且不需要实时计算和响应的任务, for example, to synchronize logs to Kafka for log analysis.
  • Asynchronous requests are possible 一直等待response响应的, and the results need to be returned to the client; while asynchronous calls, we often 会马上返回给客户端响应complete the entire request. As for the asynchronous call, the background of the task runs slowly by itself, and the client does not care.

Preface

Synchronous request
Insert picture description here
Asynchronous request
Insert picture description here
Features:

  • Yes 先释放容器分配给请求的线程与相关资源, it reduces the burden on the system and releases the request of the thread allocated by the container. The response will be delayed.后台耗时处理完成(例如长时间的运算)时再对客户端进行响应。
  • In a word: increase the server's throughput of client requests
  • ( 实际生产上我们用的比较少, If the number of concurrent requests is large, we will 通过nginx把请求负载到集群服务的各个节点上来分摊请求压力, of course, okay 通过消息队列来做请求的缓冲).

Implementation of asynchronous request

1. Servlet way to achieve asynchronous request

    @RequestMapping(value = "/email/servletReq", method = RequestMethod.GET)
    public void servletReq(HttpServletRequest request, HttpServletResponse response) {
    
    
        AsyncContext asyncContext = request.startAsync();
        //设置监听器:可设置其开始、完成、异常、超时等事件的回调处理
        asyncContext.addListener(new AsyncListener() {
    
    
            @Override
            public void onTimeout(AsyncEvent event) throws IOException {
    
    
                System.out.println("超时了...");
                //做一些超时后的相关操作...
            }

            @Override
            public void onStartAsync(AsyncEvent event) throws IOException {
    
    
                System.out.println("线程开始");
            }

            @Override
            public void onError(AsyncEvent event) throws IOException {
    
    
                System.out.println("发生错误:" + event.getThrowable());
            }

            @Override
            public void onComplete(AsyncEvent event) throws IOException {
    
    
                System.out.println("执行完成");
                //这里可以做一些清理资源的操作...
            }
        });
        
        //设置超时时间
        asyncContext.setTimeout(20000);
        asyncContext.start(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    Thread.sleep(10000);
                    System.out.println("内部线程:" + Thread.currentThread().getName());
                    asyncContext.getResponse().setCharacterEncoding("utf-8");
                    asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
                    asyncContext.getResponse().getWriter().println("这是异步的请求返回");
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                    System.out.println("异常:" + e);
                }
                //异步请求完成通知
                //此时整个请求才完成
                asyncContext.complete();
            }
        });
        //此时之类 request的线程连接已经释放了
        System.out.println("主线程:" + Thread.currentThread().getName());
    }

2.Callable

The parameters returned by the Controller用Callable包裹即可

You can inherit the WebMvcConfigurerAdapter class/implement the WebMvcConfigurer interface (SpringBoot2.0 recommended) to set the default thread pool and timeout processing

The interface uses Callable to decorate the return parameters

    @RequestMapping(value = "/email/callableReq", method = RequestMethod.GET)
    @ResponseBody
    public Callable<String> callableReq() {
    
    
        System.out.println("外部线程:" + Thread.currentThread().getName());
        return new Callable<String>() {
    
    
            @Override
            public String call() throws Exception {
    
    
                Thread.sleep(10000);
                System.out.println("内部线程:" + Thread.currentThread().getName());
                return "callable!";
            }
        };
    }

The configuration class sets the default thread pool and timeout handling

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    
    @Resource
    private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;

    @Override
    public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
    
    
        //处理 callable超时
        configurer.setDefaultTimeout(60 * 1000);
        configurer.setTaskExecutor(myThreadPoolTaskExecutor);
        configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());
    }

    @Bean
    public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {
    
    
        return new TimeoutCallableProcessingInterceptor();
    }
}

3.WebAsyncTask

将Controller返回的参数用WebAsyncTask包裹Similar to Callable usage, you can, you can also set one for WebAsyncTask 超时回调, you can achieve timeout processing

You can inherit the WebMvcConfigurerAdapter class/implement the WebMvcConfigurer interface (SpringBoot2.0 recommended) to set the default thread pool and timeout processing

    @RequestMapping(value = "/email/webAsyncReq", method = RequestMethod.GET)
    @ResponseBody
    public WebAsyncTask<String> webAsyncReq() {
    
    
        System.out.println("外部线程:" + Thread.currentThread().getName());
        Callable<String> result = () -> {
    
    
            System.out.println("内部线程开始:" + Thread.currentThread().getName());
            try {
    
    
                TimeUnit.SECONDS.sleep(4);
            } catch (Exception e) {
    
    
                    e.printStackTrace();
            }
            logger.info("副线程返回");
            System.out.println("内部线程返回:" + Thread.currentThread().getName());
            return "success";
        };
        
        
        WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);
        wat.onTimeout(new Callable<String>() {
    
    
            @Override
            public String call() throws Exception {
    
    
                return "超时";
            }
        });
        return wat;
    }

4.DeferredResult

DeferredResult can deal with some of 相对复杂一些的业务逻辑the most important is 可以在另一个线程里面进行业务处理及返回, we can communicate between two completely unrelated threads .

You can inherit the WebMvcConfigurerAdapter class/implement the WebMvcConfigurer interface (SpringBoot2.0 recommended) to set the default thread pool and timeout processing

@RequestMapping(value = "/email/deferredResultReq", method = RequestMethod.GET)
    @ResponseBody
    public DeferredResult<String> deferredResultReq() {
    
    
        System.out.println("外部线程:" + Thread.currentThread().getName());
        //设置超时时间
        DeferredResult<String> result = new DeferredResult<String>(60 * 1000L);
        //处理超时事件 采用委托机制
        result.onTimeout(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("DeferredResult超时");
                result.setResult("超时了!");
            }
        });
        result.onCompletion(new Runnable() {
    
    

            @Override
            public void run() {
    
    
                //完成后
                System.out.println("调用完成");
            }
        });

        myThreadPoolTaskExecutor.execute(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                //处理业务逻辑
                System.out.println("内部线程:" + Thread.currentThread().getName());
                //返回结果
                result.setResult("DeferredResult!!");
            }
        });
        return result;
    }

Guess you like

Origin blog.csdn.net/qq877728715/article/details/111556998