一.导学
- 使用Callable异步处理Rest服务
- 使用DeferredResult异步处理REST服务
- 异步处理配置
- 异步处理就是主线程使用委托副线程去处理业务,然后主线程去接纳其他的请求。提高性能
二.使用Callable异步处理Rest服务
- 我们先来看看模拟同步处理
@RestController
public class AsyncController {
private Logger logger = LoggerFactory.getLogger(getClass());
@RequestMapping("/order")
public String order() throws Exception {
logger.info("主线程开始");
Thread.sleep(1000);
logger.info("主线程结束");
return "success";
}
}
耗时1s
- 我们再来看看Callable异步的处理
@RestController
public class AsyncController {
private Logger logger = LoggerFactory.getLogger(getClass());
@RequestMapping("/order")
public Callable<String> order() throws Exception {
logger.info("主线程开始");
Callable<String> result = new Callable<String>() {//Callable 就是单开一个线程
@Override
public String call() throws Exception {
logger.info("副线程开始");
TimeUnit.SECONDS.sleep(1);
logger.info("副线程结束");
return "success";
}
};
logger.info("主线程结束");
return result;
}
}
输出
2019-05-11 21:41:57.324 INFO 22676 --- [nio-8070-exec-4] c.p.web.controller.AsyncController : 主线程开始
2019-05-11 21:41:57.326 INFO 22676 --- [nio-8070-exec-4] c.p.web.controller.AsyncController : 主线程结束
2019-05-11 21:41:57.358 INFO 22676 --- [ MvcAsync1] c.p.web.controller.AsyncController : 副线程开始
2019-05-11 21:41:58.359 INFO 22676 --- [ MvcAsync1] c.p.web.controller.AsyncController : 副线程结束
- 主线程和副线程是两个不同的线程,没有任何停顿 ,服务器吞吐量有个很大的提升
- 副线程处理业务逻辑时 主线程还是能够继续处理HTTP请求的
- 实际上浏览器响应还是1s
三.DeferredResult异步处理rest服务
- 在实际场景中可能会非常复杂,我们就以下单来举例,接收下单和真正处理下单逻辑的应用并不是一台服务器,如下图
- 其中线程1和线程2是隔离的 互相不知道对方的存在
- 显然Callable不能满足需要了
-
业务实现逻辑如下:
- 请求下单
- 发送消息到消息队列中
- 另外一个应用(假如是订单系统)处理下单,并返回处理结果
-
一个订单号对应一个处理结果
@Component
public class DeferredResultHolder {
private Map<String, DeferredResult<String>> map = new HashMap<>();
public Map<String, DeferredResult<String>> getMap() {
return map;
}
public void setMap(Map<String, DeferredResult<String>> map) {
this.map = map;
}
}
- 消息队列
@Component
public class MockQueue {
private Logger logger = LoggerFactory.getLogger(getClass());
private String placeOrder;// 请求下单
private String completeOrder;// 下单完成
public String getPlaceOrder() {
return placeOrder;
}
public void setPlaceOrder(String placeOrder) {
new Thread(()->{
logger.info("请求下单处理,"+placeOrder);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.placeOrder = placeOrder;
logger.info("下单请求处理完毕,"+placeOrder);
}).start();
}
public String getCompleteOrder() {
return completeOrder;
}
public void setCompleteOrder(String completeOrder) {
this.completeOrder = completeOrder;
}
}
- 监听器
@Component
public class QueueLinstener implements ApplicationListener<ContextRefreshedEvent> {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private MockQueue mockQueue;
@Autowired
private DeferredResultHolder deferredResultHolder;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
new Thread(()->{
while(true){
if(StringUtils.isNotBlank(mockQueue.getCompleteOrder())){//如果完成订单不为空
String orderNumber = mockQueue.getPlaceOrder();//获取订单号
logger.info("返回订单处理结果:"+orderNumber);
deferredResultHolder.getMap().get(orderNumber).setResult("place order success");//设置订单号处理成功
mockQueue.setCompleteOrder(null);//设置完成订单为空
deferredResultHolder.getMap().remove(orderNumber);//移除key
}else{
try {
TimeUnit.SECONDS.sleep(1);//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
- 控制器类
@RestController
public class AsyncController {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private MockQueue mockQueue;
@Autowired
private DeferredResultHolder deferredResultHolder;
@RequestMapping("/order")
public DeferredResult<String> order(){
logger.info("主线程开始");
String orderNumber = RandomStringUtils.randomNumeric(8);
mockQueue.setPlaceOrder(orderNumber);//设置订单号
DeferredResult<String> deferredResult = new DeferredResult<>();
deferredResultHolder.getMap().put(orderNumber,deferredResult);//一个订单号对应一个处理结果
logger.info("主线程结束");
return deferredResult;
}
}
输出
2019-05-11 22:30:05.272 INFO 21508 --- [nio-8070-exec-4] com.playmaker.web.async.AsyncController : 主线程开始
2019-05-11 22:30:05.276 INFO 21508 --- [nio-8070-exec-4] com.playmaker.web.async.AsyncController : 主线程结束
2019-05-11 22:30:05.276 INFO 21508 --- [ Thread-10] com.playmaker.web.async.MockQueue : 请求下单处理
2019-05-11 22:30:06.277 INFO 21508 --- [ Thread-10] com.playmaker.web.async.MockQueue : 下单请求处理完毕,55996134
2019-05-11 22:30:06.566 INFO 21508 --- [ Thread-7] com.playmaker.web.async.QueueLinstener : 返回订单处理结果:55996134
-
可以看出一共有三个线程
- 主线程接受http请求
- 第2个线程处理下单逻辑
- 第3个把第2个线程处理结果返回给前台
-
DeferredResult 在效果上实现了一个让主线程可以立即返回,但是连接不断开,可以通过DeferredResult设置返回结果来让连接返回并断开的功能
四.异步处理配置
- 相关配置
- 针对讲过的两种处理方式 拦截器 同步是直接add添加,而异步和同步不一样,异步请求需要单独用这两种方法注册拦截器的
configurer.registerCallableInterceptors(); // callable 拦截器
configurer.registerDeferredResultInterceptors(); // deferredResult拦截器
configurer.setTaskExecutor(); // 应该是自定义线程池
configurer.setDefaultTimeout() // 超时设置