SpringBoot日常:基于DeferredResult的异步处理


本章内容主要讲讲基于DeferredResult的异步处理

在 Servlet 容器中启用了异步请求处理功能,控制器方法就可以用 包装任何支持的控制器方法返回值DeferredResult,控制器可以从不同的线程异步产生返回值 — — 例如,响应外部事件(JMS 消息)、计划任务或其他事件

简单的讲述整个异步逻辑,就是一个请求过来,以往tomcat会从自身容器取出一个线程去执行逻辑,这个容器线程在逻辑没处理完之前,一直处于阻塞状态,其他请求进来也无法使用。使用本章介绍的DeferredResult后,若业务中有自定义线程池去处理逻辑,那tomcat的容器线程则可以释放,逻辑处理完回来会根据保存的上下文将结果写回到请求中去

同步阻塞:
在这里插入图片描述
异步非阻塞:
在这里插入图片描述

示例代码

@Configuration
@EnableAsync
public class AsyncConfig {

    private int corePoolSize = 16;
    
    private int maxPoolSize = 90;
    
    private int queueCapacity = 2000;

	//指定当前线程池为主线程池
    @Bean("taskExecutor")
    @Primary 
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(corePoolSize);
        taskExecutor.setMaxPoolSize(maxPoolSize);
        taskExecutor.setQueueCapacity(queueCapacity);
        taskExecutor.setKeepAliveSeconds(60);
        // 拒绝策略-抛异常
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        taskExecutor.setThreadGroupName("Task-");
        taskExecutor.setThreadNamePrefix("common-");
        taskExecutor.setBeanName("taskExecutor");
        return taskExecutor;
    }
}


@RestController
@RequestMapping("/mytest")
public class AsyncController {

	@Autowired
    private TaskExecutor taskExecutor;

	@GetMapping("/async")
    public DeferredResult<Integer> async() {
        System.out.println("当前线程 外部 " + Thread.currentThread().getName());
        DeferredResult<Integer> result = new DeferredResult<>();
        CompletableFuture.supplyAsync(this::count,taskExecutor)
                .whenCompleteAsync((res, throwable) -> {
                    if (throwable != null) {
                        result.setErrorResult(throwable.getMessage());
                    }else{
                        result.setResult(res);
                    }
                });

        return result;
    }

	private Integer count() {
        System.out.println("内部线程 名称 "+Thread.currentThread().getName());
        System.out.println("-------start---------------");
        int count = 0;
        for (int i = 0; i < 1000000; i++) {
            for (int j = 0; j < 10000; j++) {
                count = count + j;
            }
        }
        System.out.println("-------end---------------");
        return count;
    }
}

代码执行结果

在这里插入图片描述

代码执行过程解析

上述代码我们创建了一个业务线程池taskExecutor,然后controller方法在async DeferredResult内创建了一个DeferredResult对象,接着使用CompletableFuture向业务线程池taskExecutor提交我们的请求处理逻辑(其内部处理完毕后把结果设置到创建的DeferredResult),最后返回创建的DeferredResult对象。其整个处理过程如下:

1)Tomcat容器接收路径为personDeferredResult的请求后,会分配一个容器线程来执行DispatcherServlet进行请求分派,请求被分到含有/async路径的controller,然后执行async方法,该方法内创建了一个DeferredResult对象,接着把处理任务提交到了线程池进行处理,最后返回DeferredResult对象。

2)Spring MVC内部在/async方法返回后会保存DeferredResult对象到内存队列或者列表,然后会调用request.startAsync()开启异步处理,并且调用DeferredResult对象的setResultHandler方法,设置当异步结果产生后对结果进行重新路由的回调函数(逻辑在WebAsyncManager的startDeferredResultProcessing方法),接着释放分配给当前请求的容器线程,与此同时当前请求的DispatcherServlet和所有filters也执行完毕了,但是response流还是保持打开(因为任务执行结果还没写回)。

3)最终在业务线程池中执行的异步任务会产生一个结果,该结果会被设置到DeferredResult对象,然后设置的回调函数会被调用,接着Spring MVC会分派请求结果回到Servlet容器继续完成处理,DispatcherServlet被再次调用,使用返回的异步结果继续进行处理,最终把响应结果写回请求方。

DeferredResult 的优势

使用 DeferredResult 实现异步请求处理肯定有一定的优势和好处,这里总结三点

  1. 资源利用率高
    通过将耗时操作放到异步线程中执行,可以避免主线程阻塞,提高系统的并发处理能力和资源利用率。

  2. 灵活性高
    DeferredResult 允许手动设置异步处理结果,可以在多个线程中执行操作,适用于复杂的异步处理场景。

  3. 超时和异常处理
    DeferredResult 提供了超时处理和异常处理机制,允许开发者定义超时和异常处理逻辑,提高了异步请求处理的可靠性和健壮性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值