一、异步请求与异步调用的区别
两者的使用场景不同:
异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做日志分析,或保存历史数据等。
异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量.
响应不同:
异步请求是会一直等待response相应的,需要返回结果给客户端的.
异步调用我们往往会马上返回给客户端响应,完成这次整个的请求,至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。
二、代码示例
1.创建一个模仿耗时操作的业务层
@Service
public class AsyncTaskService {
public String uuid() {
try {
TimeUnit.MILLISECONDS.sleep(5000);
String nowDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:S").format(new Date());
System.out.println(nowDate + "--->工作线程(" + Thread.currentThread().getName() + ")");
} catch (InterruptedException e) {
e.printStackTrace();
}
return UUID.randomUUID().toString();
}
@Async
public String getUuid() {
try {
TimeUnit.MILLISECONDS.sleep(5000);
String nowDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:S").format(new Date());
System.out.println(nowDate + "--->工作线程(" + Thread.currentThread().getName() + ")");
} catch (InterruptedException e) {
e.printStackTrace();
}
return UUID.randomUUID().toString();
}
}
2、创建控制层列举异步请求和异步方法使用场景
@RestController
@EnableAsync
public class AsyncController {
@Autowired
AsyncTaskService asyncTaskService;
/**
* 异步方法
* @return
*/
@GetMapping("asyncMethod")
public String asyncMethod() {
System.out.println(LocalDateTime.now().toString() + "--->主线程开始");
String uuid = asyncTaskService.getUuid();
System.out.println(LocalDateTime.now().toString() + "--->主线程结束");
return uuid;
}
/**
* 阻塞请求
* @return
*/
@GetMapping("blockingRequest")
public String blockingRequest() {
System.out.println(LocalDateTime.now().toString() + "--->主线程开始");
String uuid = asyncTaskService.uuid();
System.out.println(LocalDateTime.now().toString() + "--->主线程结束");
return uuid;
}
/**
* 使用Callable
* @return
*/
@GetMapping("callable")
public Callable<String> callable() {
System.out.println(LocalDateTime.now().toString() + "--->主线程开始");
Callable<String> callable = () -> {
String uuid = asyncTaskService.uuid();
System.out.println(LocalDateTime.now().toString() + "--->子任务线程("+Thread.currentThread().getName()+")");
return uuid;
};
System.out.println(LocalDateTime.now().toString() + "--->主线程结束");
return callable;
}
/**
* 使用CompletableFuture
*
* 使用该方式时,切记自己创建执行器,不要使用内置的 ForkJoinPool线程池,会有性能问题
*
* @return
*/
@GetMapping("completableFuture")
public CompletableFuture<String> completableFuture() {
ExecutorService executor = Executors.newCachedThreadPool();
System.out.println(LocalDateTime.now().toString() + "--->主线程开始");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(asyncTaskService::uuid,executor);
System.out.println(LocalDateTime.now().toString() + "--->主线程结束");
return completableFuture;
}
/**
* 使用ListenableFuture
*
* 使用该方式时,切记自己创建执行器,不要使用内置的 ForkJoinPool线程池,会有性能问题
*
* @return
*/
@GetMapping("listenableFuture")
public ListenableFuture<String> listenableFuture() {
System.out.println(LocalDateTime.now().toString() + "--->主线程开始");
ListenableFutureTask<String> listenableFuture = new ListenableFutureTask<>(()-> asyncTaskService.uuid());
Executors.newCachedThreadPool().execute(listenableFuture);
System.out.println(LocalDateTime.now().toString() + "--->主线程结束");
return listenableFuture;
}
/**
* 使用WebAsyncTask
* @return
*/
@GetMapping("asynctask")
public WebAsyncTask asyncTask() {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
System.out.println(LocalDateTime.now().toString() + "--->主线程开始");
WebAsyncTask<String> task = new WebAsyncTask(10000L, executor, ()-> asyncTaskService.uuid());
task.onCompletion(()->{
System.out.println(LocalDateTime.now().toString() + "--->调用完成");
});
task.onTimeout(()->{
System.out.println("onTimeout");
return "onTimeout";
});
System.out.println(LocalDateTime.now().toString() + "--->主线程结束");
return task;
}
/**
* 使用DeferredResult
* @return
*/
@GetMapping("deferredResult")
public DeferredResult<String> deferredResult() {
System.out.println(LocalDateTime.now().toString() + "--->主线程("+Thread.currentThread().getName()+")开始");
DeferredResult<String> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(()->{
String uuid = asyncTaskService.uuid();
// int abc = 2/0;
return uuid;
}, Executors.newFixedThreadPool(5)).whenCompleteAsync((result, throwable)->{
if (throwable!=null) {
deferredResult.setErrorResult(throwable.getMessage());
}else {
deferredResult.setResult(result);
}
});
// 异步请求超时时调用
deferredResult.onTimeout(()->{
System.out.println(LocalDateTime.now().toString() + "--->onTimeout");
});
// 异步请求完成后调用
deferredResult.onCompletion(()->{
System.out.println(LocalDateTime.now().toString() + "--->onCompletion");
});
System.out.println(LocalDateTime.now().toString() + "--->主线程("+Thread.currentThread().getName()+")结束");
return deferredResult;
}
}
3、测试结果
①、当调用异步方法asyncMethod时,页面并未收到相应,控制台打印如下,说明方法异步执行但是不会把结果相应到客户端。
2020-04-23T13:15:29.500--->主线程开始
2020-04-23T13:15:29.500--->主线程结束
2020-04-23 13:15:34:505--->工作线程(SimpleAsyncTaskExecutor-3)
②、当调用blockingRequest时,请求被阻塞,主线程等待工作线程执行完,主线程才会结束。会占用服务器处理请求资源,每次耗时操作执行结束或主线程才会结束。
③、当调用callable及以下异步请求时,主线程不等工作线程结束就结束,工作线程相应结果也会返回客户端,客户端等待工作线程结束后才收到相应
2020-04-23T13:27:48.109--->主线程开始
2020-04-23T13:27:48.130--->主线程结束
2020-04-23 13:27:53:173--->工作线程(MvcAsync1)
2020-04-23T13:27:53.173--->子任务线程(MvcAsync1)