SpringBoot异步回调
实现后台异步处理请求,并将处理结果返回前端
Callable
使用
Callable进行回调,直接返回Callable<目标类>即可。需要进行
WebMvcConfigurer的AsyncSupportConfigurer,即MVC的异步支持配置
配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class webConfig implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
// 设定异步请求线程池callable等, spring默认线程不可重用
configurer.setTaskExecutor(asyncExecutor());
}
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
// 最大线程数
executor.setMaxPoolSize(100);
// 队列容量,如果此数字大于0,使用队列LinkedBlockingQueue
executor.setQueueCapacity(100);
// 线程名称前缀
executor.setThreadNamePrefix("WebmvcThread-");
//初始化
executor.initialize();
return executor;
}
}
Controller
import com.example.bean.Demo;
import com.example.service.TtlTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
@RestController
public class IndexController {
@Autowired
private TtlTool ttlTool;
@GetMapping("test1")
public Callable<Demo> test1() {
System.out.println("Controller方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Callable<Demo> callable = new Callable<Demo>() {
@Override
public Demo call() throws Exception {
System.out.println("Call方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Demo d = ttlTool.ttl3();
System.out.println("Call方法被 "+Thread.currentThread().getName()+" 结束执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
System.out.println();
return d;
}
};
System.out.println("Controller方法被 "+Thread.currentThread().getName()+" 结束执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
return callable;
}
}
Service
import org.springframework.stereotype.Component;
import com.example.bean.Demo;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class TtlTool {
public Demo ttl3() throws InterruptedException {
System.out.println("异步方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(5000);
Demo d = new Demo();
d.setOne("1");
d.setTwo("2");
d.setThree("3");
d.setFour("4");
System.out.println("异步方法被 "+Thread.currentThread().getName()+" 结束执行于"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
return d;
}
}
结果
同时发送两个
http://localhost:8081/test1请求
Controller方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 15:08:23
Controller方法被 http-nio-8081-exec-1 结束执行于 2023-10-20 15:08:23
Call方法被 WebmvcThread-1 开始执行于 2023-10-20 15:08:23
异步方法被 WebmvcThread-1 开始执行于 2023-10-20 15:08:23
Controller方法被 http-nio-8081-exec-2 开始执行于 2023-10-20 15:08:23
Controller方法被 http-nio-8081-exec-2 结束执行于 2023-10-20 15:08:23
Call方法被 WebmvcThread-2 开始执行于 2023-10-20 15:08:23
异步方法被 WebmvcThread-2 开始执行于 2023-10-20 15:08:23
异步方法被 WebmvcThread-1 结束执行于2023-10-20 15:08:28
Call方法被 WebmvcThread-1 结束执行于 2023-10-20 15:08:28
异步方法被 WebmvcThread-2 结束执行于2023-10-20 15:08:28
Call方法被 WebmvcThread-2 结束执行于 2023-10-20 15:08:28
并且浏览器有显示返回值

DeferredResult
一是可以封装异步请求结果返回到前端
二是允许在处理请求的方法中延迟发送响应,直到**
满足某个条件,或者达到某个时间限制**。
以下代码实现效果:
- 如果
Service的ttl1方法的执行时间**长于5秒**,就不等它,转而将ttl2方法的超时处理结果进行返回给前端显示; - 如果
Service的ttl1方法的执行时间**短于5秒**,就将ttl1方法的正常业务结果返回给前端显示; - 如果
Service的ttl1方法的执行时间**等于5秒**,返回ttl1方法的正常业务结果
配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "asyncExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
// 最大线程数
executor.setMaxPoolSize(100);
// 队列容量,如果此数字大于0,使用队列LinkedBlockingQueue
executor.setQueueCapacity(100);
// 线程名称前缀
executor.setThreadNamePrefix("WebmvcThread-");
//初始化
executor.initialize();
return executor;
}
}
Controller
import com.example.bean.Demo;
import com.example.service.TtlTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
@RestController
public class IndexController {
@Autowired
private TtlTool ttlTool;
/**
* 如果ttl1方法的执行时间短于5秒,Controller就会返回ttl1的结果
* 如果ttl1方法的执行时间大于5秒,Controller就会返回ttl2的结果
* */
@RequestMapping("/user/{name}")
public DeferredResult<Demo> demo(@PathVariable String name) throws InterruptedException, ExecutionException {
System.out.println("Controller方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//5秒之后,返回ttl2()方法结果给response
DeferredResult<Demo> deferredResult = new DeferredResult<>(5000l,ttlTool.ttl2());
CompletableFuture<Demo> d = ttlTool.ttl1();
//当d中的任务完成后,whenComplete中的逻辑才会执行
d.whenComplete(new BiConsumer<Demo, Throwable>() {
@Override
public void accept(Demo result, Throwable throwable) {
if (throwable != null) {
deferredResult.setErrorResult(throwable.getMessage());
} else {
//此方法一执行,上面的deferredResult的5秒限制就失效了
deferredResult.setResult(result);
}
}
});
System.out.println("Controller方法被 "+Thread.currentThread().getName()+" 结束执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
System.out.println();
return deferredResult;
}
}
Service
package com.example.service;
import com.example.bean.Demo;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
@Component
public class TtlTool {
@Async
public CompletableFuture<Demo> ttl1() throws InterruptedException {
System.out.println("异步方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//模拟耗时业务逻辑执行,把这里的数值改成大于5000和小于5000分别看结果
Thread.sleep(3000);
Demo d = new Demo();
d.setOne("1");
d.setTwo("2");
d.setThree("3");
d.setFour("4");
System.out.println("异步方法被 "+Thread.currentThread().getName()+" 结束执行于"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
CompletableFuture<Demo> result = CompletableFuture.completedFuture(d);
return result;
}
public Demo ttl2(){
System.out.println("超时处理方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Demo d = new Demo();
d.setOne("11");
d.setTwo("12");
d.setThree("13");
d.setFour("14");
System.out.println("超时处理方法被 "+Thread.currentThread().getName()+" 结束执行于"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
return d;
}
}
结果
业务执行时间小于
DeferredResult的超时时间:
ttl1方法执行时间小于5秒
Controller方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 15:52:56
超时处理方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 15:52:56
超时处理方法被 http-nio-8081-exec-1 结束执行于2023-10-20 15:52:56
异步方法被 WebmvcThread-1 开始执行于 2023-10-20 15:52:56
Controller方法被 http-nio-8081-exec-1 结束执行于 2023-10-20 15:52:56
异步方法被 WebmvcThread-1 结束执行于2023-10-20 15:52:59
返回的是ttl1方法的结果,即正常的业务结果

业务执行时间大于
DeferredResult的超时时间:
ttl1方法执行时间大于5秒,将上面Service代码的Thread.sleep()设置大于5秒,看结果:
Controller方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 16:09:30
超时处理方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 16:09:30
超时处理方法被 http-nio-8081-exec-1 结束执行于2023-10-20 16:09:30
异步方法被 WebmvcThread-1 开始执行于 2023-10-20 16:09:30
Controller方法被 http-nio-8081-exec-1 结束执行于 2023-10-20 16:09:30
异步方法被 WebmvcThread-1 结束执行于2023-10-20 16:09:36
返回的是ttl2方法的结果,即DeferredResult的超时结果

业务执行时间等于
DeferredResult的超时时间:
ttl1方法执行时间等于5秒,将上面Service代码的Thread.sleep()设置为5秒,看结果:
Controller方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 16:11:16
超时处理方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 16:11:16
超时处理方法被 http-nio-8081-exec-1 结束执行于2023-10-20 16:11:16
异步方法被 WebmvcThread-1 开始执行于 2023-10-20 16:11:16
Controller方法被 http-nio-8081-exec-1 结束执行于 2023-10-20 16:11:16
异步方法被 WebmvcThread-1 结束执行于2023-10-20 16:11:21
返回的是ttl1方法的结果,即正常的业务结果

Future及其派生
Future以及它的子类CompletableFuture都可以进行回调返回
CompletableFuture更加灵活,优于Future
配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "asyncExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
// 最大线程数
executor.setMaxPoolSize(100);
// 队列容量,如果此数字大于0,使用队列LinkedBlockingQueue
executor.setQueueCapacity(100);
// 线程名称前缀
executor.setThreadNamePrefix("WebmvcThread-");
//初始化
executor.initialize();
return executor;
}
}
Controller
import com.example.bean.Demo;
import com.example.service.TtlTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
@RestController
public class IndexController {
@Autowired
private TtlTool ttlTool;
@RequestMapping("/user/{name}")
public Future<Demo> demo(@PathVariable String name) throws InterruptedException, ExecutionException {
System.out.println("Controller方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//返回CompletableFuture也可以,把方法上面的Future<Demo>也改成CompletableFuture
//CompletableFuture<Demo> d = ttlTool.ttl1();
Future<Demo> d = ttlTool.ttl1();
System.out.println("Controller方法被 "+Thread.currentThread().getName()+" 结束执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
return d;
}
}
Service
import com.example.bean.Demo;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
@Component
public class TtlTool {
@Async
public CompletableFuture<Demo> ttl1() throws InterruptedException {
System.out.println("异步方法被 "+Thread.currentThread().getName()+" 开始执行于 "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(3000);
Demo d = new Demo();
d.setOne("1");
d.setTwo("2");
d.setThree("3");
d.setFour("4");
System.out.println("异步方法被 "+Thread.currentThread().getName()+" 结束执行于"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
CompletableFuture<Demo> result = CompletableFuture.completedFuture(d);
return result;
}
}
结果
Controller方法被 http-nio-8081-exec-1 开始执行于 2023-10-20 16:30:25
Controller方法被 http-nio-8081-exec-1 结束执行于 2023-10-20 16:30:25
异步方法被 WebmvcThread-1 开始执行于 2023-10-20 16:30:25
异步方法被 WebmvcThread-1 结束执行于2023-10-20 16:30:28
前端有返回

三者区别
Callable
比较简单,直接在call方法中写任务即可,结合WebMvcConfigurer配置实现请求异步处理
DeferredResult
真正的作用是延迟返回。
在异步任务处理完成后,可以使用DeferredResult的setResult方法封装异步结果,这样Controller就会在setResult方法执行后才进行结果返回。除此之外,还可以通过DeferredResult的构造方法设置超时时间,实现异步处理的超时兜底。
值得注意的是,在使用DeferredResult进行结果返回时,会开启一个新的tomcat线程进行返回,前面执行Controller请求的线程会被释放。
一般使用自己配置的异步线程池执行任务,并使用setResult方法设置结果,这样用于返回结果的tomcat线程就不会涉及到耗时处理。从而节省资源。
Future及其派生
其实主要是它的子类CompletableFuture
CompletableFuture可以对多种有参无参以及有无返回值的情况处理,还可以组合多个异步任务执行,非常灵活。而Callable无法做到这一点CompletableFuture可以创建异步任务,Future只能接收异步结果
Callable和Future区别
在Java中,Callable和Future是用于支持异步计算和获取结果的关键接口。它们之间的区别如下:
- 功能不同:
Callable接口代表一个可以返回结果的异步计算任务,它的call()方法可以在计算完成后返回一个结果。Callable通常被用于ExecutorService中提交任务。Future接口代表一个异步计算的结果,它提供了一些方法来检查任务是否完成、获取任务的计算结果或取消任务的执行。
- 返回值类型不同:
Callable的call()方法定义了一个泛型返回值类型,可以通过Future<T>来获取计算结果,其中T表示计算结果的类型。Future的get()方法返回一个泛型类型的结果,可以是Callable的计算结果或者抛出的异常。
- 异步处理方式不同:
Callable是一个主动执行的接口,任务执行完成后会返回结果。Future是一个被动获取结果的接口,可以通过调用get()方法来等待并获取异步任务的结果。
- 取消任务的能力不同:
Callable接口没有提供取消任务的功能。Future接口提供了cancel()方法来取消异步任务的执行。
综上所述,Callable和Future是Java中用于支持异步计算的接口,Callable用于定义异步计算任务,而Future用于获取异步计算的结果,并提供了一些其他的方法来管理和取消任务的执行。
1845

被折叠的 条评论
为什么被折叠?



