springboot(14)--异步任务

        在Servlet3.0以前,Servlet采用Thread-Per-Request的方式处理请求,即一次Http请求都由某一个线程从头到尾负责处理。如果一个请求需要进行IO操作(I/O操作非常耗时),比如访问数据库、调用第三方服务接口、网络I/O等,那么处理请求的线程将一直阻塞,直到I/O操作完成才继续处理请求。当并发量很大的时候,这将带来严重的性能问题。如下图所示:

springboot(14)--异步任务_第1张图片

Servlet3.0以后,可以从HttpServletRequest对象中获得一个AsyncContext对象,该对象构成了异步处理的上下文,并含有Request、Response对象的引用。获取AsyncContext对象并从当前Controller返回后,AsyncContext对象将被送到另一个线程(一般是线程池)中取执行,并在心的线程中返回结果给客户端。如图所示:

springboot(14)--异步任务_第2张图片

 


多说几句:

随着Spring5发布,提供了一个响应式Web框架:Spring WebFlux。之后可能就不需要Servlet容器的支持了。以下是其先后对比图:

springboot(14)--异步任务_第3张图片

左侧是传统的基于ServletSpring Web MVC框架,右侧是5.0版本新引入的基于Reactive StreamsSpring WebFlux框架,从上到下依次是Router FunctionsWebFluxReactive Streams三个新组件。

对于其发展前景还是拭目以待吧。有时间也该去了解下Spring5了。

————————————————————————————————————————————————

说明:

  1. onStartAsync:异步线程开始时调用
  2. onError:异步线程出错时调用
  3. onTimeout:异步线程执行超时调用
  4. onComplete:异步执行完毕时调用

需要注意的是只有在调用request.startAsync前将监听器添加到AsyncContext,监听器的onStartAsync方法才会起作用,而调用startAsyncAsyncContext还不存在,所以第一次调用startAsync是不会被监听器中的onStartAsync方法捕获的,只有在超时后又重新开始的情况下onStartAsync方法才会起作用。

一定要设置超时时间,不能无限等待下去,不然和正常的请求就一样了。。

 

Servlet实现异步请求

0.控制器

@RestController
@RequestMapping("/servlet")
public class ServletController {

    private static Logger logger=Logger.getLogger(ServletController.class);

    @RequestMapping("/async")
    public void toDoAsync(HttpServletRequest request, HttpServletResponse response){
        AsyncContext asyncContext=request.startAsync();
        asyncContext.setTimeout(2000);
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                logger.info("完成异步任务");
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                logger.info("超时咯!");
            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
                logger.info("出错咯!");
            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                logger.info("开始咯!");
            }
        });

        logger.info("线程:"+Thread.currentThread().getName());
        asyncContext.start(()->{
            try {
                Thread.sleep(1000);
                logger.info("内部线程:"+Thread.currentThread().getName());
                asyncContext.getResponse().setCharacterEncoding("utf-8");
                asyncContext.getResponse().setContentType("text/html;character=utf-8");
                asyncContext.getResponse().getWriter().println("这是【异步】的请求返回");
            } catch (Exception e) {
                e.printStackTrace();
            }
            asyncContext.complete();
        });
        logger.info("线程:"+Thread.currentThread().getName());
    }

}

使用过滤器、Servlet时,需要加入asyncSupportedtrue配置,开启异步请求支持。

 

Spring实现异步请求

在spring中有三种方式实现异步请求:DeferredResult、Callable、WebAsyncTask。以后再去了解它们适用的不同场景。

Callable

0.创建CallableConfig

主要就是配置线程池和配置超时时间、超时处理等。

@Configuration
public class AsyncConfig implements WebMvcConfigurer {

    @Bean("asyncThreadPoolExecutor")
    public ThreadPoolTaskExecutor getThreadPool(){
        ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(1);
        executor.setKeepAliveSeconds(200);
        executor.setThreadNamePrefix("callable-");
        executor.initialize();
        return executor;
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setDefaultTimeout(60*1000);
        configurer.setTaskExecutor(getThreadPool());
    }
}

1.创建控制器

返回的Callable将交给线程池处理:

@RestController
@RequestMapping("/spring")
public class SpringAsyncController {

    private static Logger logger=Logger.getLogger(SpringAsyncController.class);

    @GetMapping("/callable")
    public Callable callable() {
        logger.info("外部线程:"+Thread.currentThread().getName());
        return new Callable() {
            @Override
            public String call() throws Exception {
                logger.info("内部线程:"+Thread.currentThread().getName());
                return "callable!";
            }
        };

    }
}

2.设置Filter支持异步任务

@WebFilter(asyncSupported = true)

DefferedResult

相比于callableDeferredResult可以处理一些相对复杂一些的业务逻辑,最主要还是可以在另一个线程里面进行业务处理及返回,即可在两个完全不相干的线程间的通信。

@RestController
@RequestMapping("/spring")
public class SpringAsyncController {

    private static Logger logger=Logger.getLogger(SpringAsyncController.class);
    public static ExecutorService FIXED_THREAD_POOL = Executors.newFixedThreadPool(30);

    @GetMapping("/defferredresult")
    public DeferredResult deferredResult(){
        logger.info("外部线程:"+Thread.currentThread().getName());
        DeferredResult result=new DeferredResult(60*1000L);
        result.onTimeout(()->{
            logger.info("超时啦");
            result.setResult("超时咯");
        });
        result.onCompletion(()->{
            logger.info("完成咯");
        });
        FIXED_THREAD_POOL.execute(()->{
            logger.info("内部线程:"+Thread.currentThread().getName());
            result.setResult("DeferredResult");
        });
        return result;
    }
}

WebSyncTask

0.和Callable一样创建Config

1.创建Controller

@RestController
@RequestMapping("/spring")
public class SpringAsyncController {

    private static Logger logger=Logger.getLogger(SpringAsyncController.class);
    
    @GetMapping("/websynctask")
    public WebAsyncTask webAsyncTask(){
        logger.info("外部线程");
        WebAsyncTask webAsyncTask=new WebAsyncTask(6 * 1000L, new Callable() {
            @Override
            public String call() throws Exception {
                logger.info("内部线程"+Thread.currentThread().getName());
                return "webAsyncTask";
            }
        });
        webAsyncTask.onTimeout(()->{
            logger.info("超时啦");
            return "WebAsyncTasl超时咯";
        });
        webAsyncTask.onCompletion(()->{
            logger.info("完成咯");
        });
        return webAsyncTask;
    }
}

 

 

 

 

 

你可能感兴趣的:(spring系列)