😆Purpose
详细介绍请看Spring DeferredResult 如何实现异步化,从而大大增加长连接数量.
DeferredResultprovides an alternative to using aCallablefor asynchronous request processing. While aCallableis executed concurrently onbehalf of(代表) the application, with aDeferredResultthe application can produce the result from a thread of its choice.
DeferredResult提供了一种使用Callable接口的方式来实现异步请求 . 虽然Callable是代表应用程序并发执行的,但是使用DeferredResult application可以得到结果从它执行的线程中。
😛Prepare to using the DeferredResult application
// 我目前捕捉不到 他的 onError()事件
@Slf4j
@CrossOrigin
@RestController
@SpringBootApplication
public class DeferredresultSpringmvcApplication {
public static void main(String[] args) {
SpringApplication.run(DeferredresultSpringmvcApplication.class, args);
}
private static ExecutorService executors = Executors.newFixedThreadPool(10);
@RequestMapping("/get0")
public DeferredResult<HashMap<String, Object>> getData() {
log.info("controller 调用的 thread : {}",Thread.currentThread().getName());
log.info("controller 开始执行 ");
// 设置超时时间 ,其实只有异步有效 我测试了 ,同步根本不会执行
DeferredResult<HashMap<String, Object>> result = new DeferredResult<>(1000L);
result.setResult(getMap());
//如果不是线程执行 , 则返回true
executors.execute(new Runnable() {
@Override
public void run() {
// 结果是处理是否成功 有返回结果直接返回 success , 如果 异步请求已过期(必须是异步处理 才会生效) 返回false
boolean isok = false;
isok = result.setResult(getMap());
log.info("DeferredResult 处理是否成功 : {}", isok);
// 成功以后的处理结果 (必须 放到 调用的 Runnable接口里)
result.setResultHandler(new DeferredResult.DeferredResultHandler() {
@Override
public void handleResult(Object result) {
log.info("DeferredResult 处理结果 : {} ",result.toString());
}
});
}
});
// 我这里有问题 // 我调用线程异常全部都是在 超时里面
// 处理异常 , 我捕捉不到这个 异常 ,不知道 哪个地方的 , 其实可以自定义异常 ,然后 根据异常类型进行判断
result.onError((e) -> {
log.info("onError");
});
// 完成执行 , 处理结果不管是啥 都会执行
result.onCompletion(new Runnable() {
@Override
public void run() {
log.info("处理 onCompletion");
}
});
// 处理超时的处理 , 只有异步结果才会处理
result.onTimeout(new Runnable() {
@Override
public void run() {
log.info("处理超时 onTimeout");
}
});
log.info("controller 执行完毕 ");
// 记住 只能返回 result , 不能返回null
return result;
}
/**
* 不用线程池执行
* @return
*/
@RequestMapping("/get1")
public DeferredResult<HashMap<String, Object>> getByNoExecutor(){
log.info("controller 调用的 thread : {}",Thread.currentThread().getName());
log.info("controller 开始执行 ");
DeferredResult<HashMap<String, Object>> result = new DeferredResult<>(3000L);
result.setResult(getMap());
log.info("controller 执行完毕 ");
return result;
}
/**
* 用线程池执行
* @return
*/
@RequestMapping("/get2")
public DeferredResult<HashMap<String, Object>> getByExecutor(){
log.info("controller 调用的 thread : {}",Thread.currentThread().getName());
log.info("controller 开始执行 ");
DeferredResult<HashMap<String, Object>> result = new DeferredResult<>(3000L);
executors.execute(new Runnable() {
@Override
public void run() {
result.setResult(getMap());
}
});
log.info("controller 执行完毕 ");
return result;
}
/**
* 模拟service 服务
* @return
*/
public HashMap<String, Object> getMap(){
log.info("service 开始执行 ");
// 模拟 service 延迟服务
HashMap<String, Object> map = new HashMap<>();
try {
Thread.sleep(2000);
// int i = 1/0;
map.put("key",Thread.currentThread().getName());
log.info("service 服务调用的线程 : {} ",Thread.currentThread().getName());
} catch (Exception e) {
throw new RuntimeException();
}
log.info("service 执行完毕 ");
return map;
}
}
我们看打印结果 发现
1. get1 执行结果
2019-10-12 12:44:17.549 INFO 2020 --- [nio-8080-exec-2] c.s.DeferredresultSpringmvcApplication : controller 调用的 thread : http-nio-8080-exec-2
2019-10-12 12:44:17.549 INFO 2020 --- [nio-8080-exec-2] c.s.DeferredresultSpringmvcApplication : controller 开始执行
2019-10-12 12:44:17.549 INFO 2020 --- [nio-8080-exec-2] c.s.DeferredresultSpringmvcApplication : service 开始执行
2019-10-12 12:44:19.549 INFO 2020 --- [nio-8080-exec-2] c.s.DeferredresultSpringmvcApplication : service 服务调用的线程 : http-nio-8080-exec-2
2019-10-12 12:44:19.549 INFO 2020 --- [nio-8080-exec-2] c.s.DeferredresultSpringmvcApplication : service 执行完毕
2019-10-12 12:44:19.549 INFO 2020 --- [nio-8080-exec-2] c.s.DeferredresultSpringmvcApplication : controller 执行完毕
2. get2执行结果
2019-10-12 12:44:47.993 INFO 2020 --- [nio-8080-exec-4] c.s.DeferredresultSpringmvcApplication : controller 调用的 thread : http-nio-8080-exec-4
2019-10-12 12:44:47.993 INFO 2020 --- [nio-8080-exec-4] c.s.DeferredresultSpringmvcApplication : controller 开始执行
2019-10-12 12:44:47.994 INFO 2020 --- [nio-8080-exec-4] c.s.DeferredresultSpringmvcApplication : controller 执行完毕
2019-10-12 12:44:47.997 INFO 2020 --- [pool-1-thread-1] c.s.DeferredresultSpringmvcApplication : service 开始执行
2019-10-12 12:44:49.999 INFO 2020 --- [pool-1-thread-1] c.s.DeferredresultSpringmvcApplication : service 服务调用的线程 : pool-1-thread-1
2019-10-12 12:44:49.999 INFO 2020 --- [pool-1-thread-1] c.s.DeferredresultSpringmvcApplication : service 执行完毕
😙那我们如何自己实现一个呢 ?
简单实现
/**
* 自定义简单使用
* @return
*/
@RequestMapping("/get3")
public MyCallable<HashMap<String, Object>> get(){
log.info("controller 调用的 thread : {}",Thread.currentThread().getName());
log.info("controller 开始执行 ");
MyCallable<HashMap<String, Object>> objectMyCallable = new MyCallable<>();
log.info("controller 执行完毕 ");
return objectMyCallable;
}
/**
* 简单的执行逻辑
* @param <T>
*/
private class MyCallable<T> implements Callable<T> {
@Override
public T call() throws Exception {
return (T)getMap();
}
}
/**
* 模拟service 服务
* @return
*/
public HashMap<String, Object> getMap(){
log.info("service 开始执行 ");
// 模拟 service 延迟服务
HashMap<String, Object> map = new HashMap<>();
try {
Thread.sleep(2000);
// int i = 1/0;
map.put("key",Thread.currentThread().getName());
log.info("service 服务调用的线程 : {} ",Thread.currentThread().getName());
} catch (Exception e) {
throw new RuntimeException();
}
log.info("service 执行完毕 ");
return map;
}
我们看一下我们实现的 执行结果 , 发现执行的是 task 这个线程 ,这个线程池到底属于MyCallable维护的.
2019-10-12 12:42:23.869 INFO 2020 --- [nio-8080-exec-5] c.s.DeferredresultSpringmvcApplication : controller 调用的 thread : http-nio-8080-exec-5
2019-10-12 12:42:23.870 INFO 2020 --- [nio-8080-exec-5] c.s.DeferredresultSpringmvcApplication : controller 开始执行
2019-10-12 12:42:23.872 INFO 2020 --- [nio-8080-exec-5] c.s.DeferredresultSpringmvcApplication : controller 执行完毕
2019-10-12 12:42:23.875 INFO 2020 --- [ task-1] c.s.DeferredresultSpringmvcApplication : service 开始执行
2019-10-12 12:42:25.875 INFO 2020 --- [ task-1] c.s.DeferredresultSpringmvcApplication : service 服务调用的线程 : task-1
2019-10-12 12:42:25.875 INFO 2020 --- [ task-1] c.s.DeferredresultSpringmvcApplication : service 执行完毕
如果用线程池呢, 那么我们可以自定义我们的线程池,不让task线程,执行复杂的代码,让我们的线程池执行,
/**
* 自定义灵活性 增加代码的可拓展性,加入线程池
* @return
*/
@RequestMapping("/get4")
public MyCallable2<HashMap<String, Object>> get2(){
log.info("controller 调用的 thread : {}",Thread.currentThread().getName());
log.info("controller 开始执行 ");
MyCallable2<HashMap<String, Object>> objectMyCallable = new MyCallable2<>(new Callable<HashMap<String, Object>>() {
@Override
public HashMap<String, Object> call() throws Exception {
return getMap();
}
});
log.info("controller 执行完毕 ");
return objectMyCallable;
}
/**
* 这里 其实 就是 一个 deferred result
* @param <T>
*/
private class MyCallable2<T> implements Callable<T> {
private ExecutorService executors = Executors.newFixedThreadPool(10);
private Callable<T> callable;
public MyCallable2(Callable<T> callable) {
this.callable = callable;
}
// 让一个新的线程执行 callable中的方法
@Override
public T call() throws Exception {
Future<T> submit = executors.submit(callable);
return submit.get();
}
}
/**
* 模拟service 服务
* @return
*/
public HashMap<String, Object> getMap(){
log.info("service 开始执行 ");
// 模拟 service 延迟服务
HashMap<String, Object> map = new HashMap<>();
try {
Thread.sleep(2000);
// 这里异常我没有捕捉到
// int i = 1/0;
map.put("key",Thread.currentThread().getName());
log.info("service 服务调用的线程 : {} ",Thread.currentThread().getName());
} catch (Exception e) {
throw new RuntimeException();
}
log.info("service 执行完毕 ");
return map;
}
执行结果
2019-10-12 12:42:44.879 INFO 2020 --- [nio-8080-exec-8] c.s.DeferredresultSpringmvcApplication : controller 调用的 thread : http-nio-8080-exec-8
2019-10-12 12:42:44.880 INFO 2020 --- [nio-8080-exec-8] c.s.DeferredresultSpringmvcApplication : controller 开始执行
2019-10-12 12:42:44.882 INFO 2020 --- [nio-8080-exec-8] c.s.DeferredresultSpringmvcApplication : controller 执行完毕
2019-10-12 12:42:44.883 INFO 2020 --- [pool-2-thread-1] c.s.DeferredresultSpringmvcApplication : service 开始执行
2019-10-12 12:42:46.884 INFO 2020 --- [pool-2-thread-1] c.s.DeferredresultSpringmvcApplication : service 服务调用的线程 : pool-2-thread-1
2019-10-12 12:42:46.884 INFO 2020 --- [pool-2-thread-1] c.s.DeferredresultSpringmvcApplication : service 执行完毕
😜Summary
感觉我写的很详细 , 其他的就不多说了 , 其实他的应用场景就是 当一个处理请求消耗很长的时间时 , 需要去使用这个 , 主要就是采用返回一个处理代码逻辑线程, 不是返回一个结果 , 这点要明白. 因为A线程是不会等待B线程执行完,才能拿到他的结果,如果A必须拿到B所返回的结果,那么A必须阻塞. 其实 这点跟
deferedresult很相似,他不会将监听80端口socket的线程等待你所要处理线程完成,就直接给你返回了你的处理线程,前提是你自己维护一个线程池.不然还是socket线程去执行.