1、使用Runnable
2、使用DeferredResult
3、异步处理的一些配置
正常请求方式
packagecom.nxz.controller;importlombok.extern.slf4j.Slf4j;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.concurrent.Callable;/*** 异步处理的controller*/@RestController
@Slf4jpublic classAsyncController {//标准的同步处理逻辑
@RequestMapping("/order")public String order() throwsInterruptedException {
log.info("主线城开始");
Thread.sleep(1000);//具体的业务逻辑
log.info("主线程返回");return "success";
}
}
1、通过callable异步方式
packagecom.nxz.controller;importlombok.extern.slf4j.Slf4j;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.concurrent.Callable;/*** 异步处理的controller*/@RestController
@Slf4jpublic classAsyncController {//异步处理方式
@RequestMapping("/order1")public Callable order1() throwsInterruptedException {
log.info("主线城开始");
Callable callable = new Callable() {
@Overridepublic String call() throwsException {
log.info("副线程线程开始 callable.call()");
Thread.sleep(1000);//具体的业务逻辑
log.info("副线程线程结束 callable.call()");return "success";
}
};
log.info("主线程返回");returncallable;
}
}
访问order1后日志输出:日志表明主线程返回就代表请求已经结束,但是具体的数据信息是在副线程结束时
才返回的(也就是在主线程结束后tomcat等中间件是可以接受其他http请求,增大了吞吐量)
2019-04-29 20:28:32.433 INFO 16788 --- [nio-8080-exec-4] com.nxz.controller.AsyncController : 主线城开始2019-04-29 20:28:32.434 INFO 16788 --- [nio-8080-exec-4] com.nxz.controller.AsyncController : 主线程返回2019-04-29 20:28:32.434 INFO 16788 ---[ MvcAsync2] com.nxz.controller.AsyncController : 副线程线程开始 callable.call()2019-04-29 20:28:33.435 INFO 16788 --- [ MvcAsync2] com.nxz.controller.AsyncController : 副线程线程结束 callable.call()
浏览器显示结果时间
2、DeferredResult形式
runnable形式的缺点:副线程的发起必须是在主线程下,但是企业级开发时,是比较复杂的。
DeferredResult是在应用1接受http请求后,由线程1将请求放到消息队列中,然后又另一台服务器具体启用副线程执行逻辑,处理完成之后由线程二监听消息队列,接受返回数据,返回给前端
controller:
@AutowiredprivateMockQueue mockQueue;
@AutowiredprivateDeferredResultHolder deferredResultHolder;//在主线程中是看不到副线程的任何东西的
@RequestMapping("/order2")public DeferredResult order2() throwsInterruptedException {
log.info("主线程开始");
String orderNum= RandomStringUtils.randomNumeric(8);//模拟订单号
mockQueue.setPlaceOrder(orderNum);//模拟消息队列(将订单号放到消息队里中)
DeferredResult result = new DeferredResult<>();
deferredResultHolder.getMap().put(orderNum, result);
log.info("主线程结束");returnresult;
}
模拟队列:
packagecom.nxz.async;importlombok.Getter;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;/*** 模拟队列的对象*/@Getter
@Component
@Slf4jpublic classMockQueue {//代表接受的信息
privateString placeOrder;//代表返回的消息
privateString complateOrder;//set 方法模拟往消息队列中放消息
public void setPlaceOrder(String placeOrder) throwsInterruptedException {new Thread(() ->{
log.info("接到请求消息" +placeOrder);try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}this.complateOrder =placeOrder;
log.info("接到请求消息处理完成" +placeOrder);
}).start();
}public voidsetComplateOrder(String complateOrder) {this.complateOrder =complateOrder;
}
}
异步监听处理结果:
packagecom.nxz.async;importlombok.extern.slf4j.Slf4j;importorg.apache.commons.lang.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.ApplicationListener;importorg.springframework.context.event.ContextRefreshedEvent;importorg.springframework.stereotype.Component;//监听器
@Component
@Slf4jpublic class QueueListener implements ApplicationListener{
@AutowiredprivateMockQueue mockQueue;
@AutowiredprivateDeferredResultHolder deferredResultHolder;/*** ContextRefreshedEvent这个事件是spring初始化完毕的一个事件
* 监听这个事件就是为了 在系统启动完毕后要做什么事
*
*@paramcontextRefreshedEvent*/@Overridepublic voidonApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
log.info("接受返回结果的listener");new Thread(() ->{//将以下while循环放到一个单开的thred线程 防止主线程死循环//监听mockqueue中的complateOrder
while (true) {if(StringUtils.isNotBlank(mockQueue.getComplateOrder())) {
String orderNum=mockQueue.getComplateOrder();//返回订单处理结果
log.info("返回订单处理结果" +orderNum);
deferredResultHolder.getMap().get(orderNum).setResult("place order success");
mockQueue.setComplateOrder(null);//表名任务已经处理完了
} else{//complateorder中没有值是睡眠100毫秒
try{
log.info("没有任务休眠100毫秒");
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
发送一个请求后,日志输出:
2019-04-29 21:27:18.959 INFO 7176 --- [nio-8080-exec-3] com.nxz.controller.AsyncController : 主线程开始2019-04-29 21:27:18.960 INFO 7176 --- [nio-8080-exec-3] com.nxz.controller.AsyncController : 主线程结束2019-04-29 21:27:18.960 INFO 7176 --- [ Thread-43] com.nxz.async.MockQueue : 接到请求消息763116042019-04-29 21:27:19.961 INFO 7176 --- [ Thread-43] com.nxz.async.MockQueue : 接到请求消息处理完成763116042019-04-29 21:27:21.242 INFO 7176 --- [ Thread-30] com.nxz.async.QueueListener : 返回订单处理结果76311604