如何异步的处理restful服务(基础)
1、使用Runnable
2、使用DeferredResult
3、异步处理的一些配置
正常请求方式
package com.nxz.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.Callable; /** * 异步处理的controller */ @RestController @Slf4j public class AsyncController { //标准的同步处理逻辑 @RequestMapping("/order") public String order() throws InterruptedException { log.info("主线城开始"); Thread.sleep(1000);//具体的业务逻辑 log.info("主线程返回"); return "success"; } }
1、通过callable异步方式
package com.nxz.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.Callable; /** * 异步处理的controller */ @RestController @Slf4j public class AsyncController { //异步处理方式 @RequestMapping("/order1") public Callable<String> order1() throws InterruptedException { log.info("主线城开始"); Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { log.info("副线程线程开始 callable.call()"); Thread.sleep(1000);//具体的业务逻辑 log.info("副线程线程结束 callable.call()"); return "success"; } }; log.info("主线程返回"); return callable; } }
访问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:
@Autowired private MockQueue mockQueue; @Autowired private DeferredResultHolder deferredResultHolder; //在主线程中是看不到副线程的任何东西的 @RequestMapping("/order2") public DeferredResult<String> order2() throws InterruptedException { log.info("主线程开始"); String orderNum = RandomStringUtils.randomNumeric(8);//模拟订单号 mockQueue.setPlaceOrder(orderNum);//模拟消息队列(将订单号放到消息队里中) DeferredResult<String> result = new DeferredResult<>(); deferredResultHolder.getMap().put(orderNum, result); log.info("主线程结束"); return result; }
模拟队列:
package com.nxz.async; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * 模拟队列的对象 */ @Getter @Component @Slf4j public class MockQueue { //代表接受的信息 private String placeOrder; //代表返回的消息 private String complateOrder; //set 方法模拟往消息队列中放消息 public void setPlaceOrder(String placeOrder) throws InterruptedException { new Thread(() -> { log.info("接到请求消息" + placeOrder); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.complateOrder = placeOrder; log.info("接到请求消息处理完成" + placeOrder); }).start(); } public void setComplateOrder(String complateOrder) { this.complateOrder = complateOrder; } }
异步监听处理结果:
package com.nxz.async; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; //监听器 @Component @Slf4j public class QueueListener implements ApplicationListener<ContextRefreshedEvent> { @Autowired private MockQueue mockQueue; @Autowired private DeferredResultHolder deferredResultHolder; /** * ContextRefreshedEvent这个事件是spring初始化完毕的一个事件 * 监听这个事件就是为了 在系统启动完毕后要做什么事 * * @param contextRefreshedEvent */ @Override public void onApplicationEvent(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 : 接到请求消息76311604 2019-04-29 21:27:19.961 INFO 7176 --- [ Thread-43] com.nxz.async.MockQueue : 接到请求消息处理完成76311604 2019-04-29 21:27:21.242 INFO 7176 --- [ Thread-30] com.nxz.async.QueueListener : 返回订单处理结果76311604