SpringMVC异步处理(Callable和DeferredResult)

亦凉 2022-05-19 12:49 305阅读 0赞

官方文档中说DeferredResult和Callable都是为了异步生成返回值提供基本的支持。简单来说就是一个请求进来,如果你使用了DeferredResult或者Callable,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,这个DispatcherServlet就会被再次调用并且处理,以异步产生的方式,向请求端返回值。
这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量。

Callable

  1. @GetMapping("/callable")
  2. public Callable<String> testCallable() throws InterruptedException {
  3. log.info("主线程开始!");
  4. Callable<String> result = new Callable<String>() {
  5. @Override
  6. public String call() throws Exception {
  7. log.info("副线程开始!");
  8. Thread.sleep(1000);
  9. log.info("副线程结束!");
  10. return "SUCCESS";
  11. }
  12. };
  13. log.info("主线程结束!");
  14. return result;
  15. }

输出的结果如下:

  1. 主线程开始!
  2. 主线程结束!
  3. 副线程开始!
  4. 副线程结束!

主线程会提前返回,可以处理其他请求,等待有结果之后再输出结果
这里写图片描述

DeferredResult

一旦启用了异步请求处理功能 ,控制器就可以将返回值包装在DeferredResult,控制器可以从不同的线程异步产生返回值。优点就是可以实现两个完全不相干的线程间的通信。
我们模拟如下场景:
这里写图片描述

由于消息队列和应用2的部分太繁琐,我们使用一个类来代替

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class MockQueue {
  6. private String placeOrder;
  7. private String completeOrder;
  8. private Logger logger = LoggerFactory.getLogger(getClass());
  9. public String getPlaceOrder() {
  10. return placeOrder;
  11. }
  12. public void setPlaceOrder(String placeOrder) throws Exception {
  13. new Thread(() -> {
  14. logger.info("接到下单请求, " + placeOrder);
  15. try {
  16. Thread.sleep(1000);
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. this.completeOrder = placeOrder;
  21. logger.info("下单请求处理完毕," + placeOrder);
  22. }).start();
  23. }
  24. public String getCompleteOrder() {
  25. return completeOrder;
  26. }
  27. public void setCompleteOrder(String completeOrder) {
  28. this.completeOrder = completeOrder;
  29. }
  30. }

定义一个Controller即线程1的部分

  1. import org.apache.commons.lang.RandomStringUtils;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.springframework.web.context.request.async.DeferredResult;
  8. @RestController
  9. public class AsyncController {
  10. @Autowired
  11. private MockQueue mockQueue;
  12. @Autowired
  13. private DeferredResultHolder deferredResultHolder;
  14. private Logger logger = LoggerFactory.getLogger(getClass());
  15. @RequestMapping("/order")
  16. public DeferredResult<String> order() throws Exception {
  17. logger.info("主线程开始");
  18. String orderNumber = RandomStringUtils.randomNumeric(8);
  19. mockQueue.setPlaceOrder(orderNumber);
  20. DeferredResult<String> result = new DeferredResult<>();
  21. deferredResultHolder.getMap().put(orderNumber, result);
  22. return result;
  23. }
  24. }

定义一个类,用来线程1和线程2之间通信的,使用@Component默认为单例,方便通信。

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.context.request.async.DeferredResult;
  5. @Component
  6. public class DeferredResultHolder {
  7. private Map<String, DeferredResult<String>> map = new HashMap<String, DeferredResult<String>>();
  8. public Map<String, DeferredResult<String>> getMap() {
  9. return map;
  10. }
  11. public void setMap(Map<String, DeferredResult<String>> map) {
  12. this.map = map;
  13. }
  14. }

定义一个类来监听订单是否处理完,如果处理完了的话就设置deferredResultHolder中的DeferredResult的值,就会返回结果了。

  1. import org.apache.commons.lang.StringUtils;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.context.ApplicationListener;
  6. import org.springframework.context.event.ContextRefreshedEvent;
  7. import org.springframework.stereotype.Component;
  8. @Component
  9. public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
  10. @Autowired
  11. private MockQueue mockQueue;
  12. @Autowired
  13. private DeferredResultHolder deferredResultHolder;
  14. private Logger logger = LoggerFactory.getLogger(getClass());
  15. @Override
  16. public void onApplicationEvent(ContextRefreshedEvent event) {
  17. new Thread(() -> { while (true) { if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) { String orderNumber = mockQueue.getCompleteOrder(); logger.info("返回订单处理结果:"+orderNumber); deferredResultHolder.getMap().get(orderNumber).setResult("place order success"); mockQueue.setCompleteOrder(null); }else{ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }

发表评论

表情:
评论列表 (有 0 条评论,305人围观)

还没有评论,来说两句吧...

相关阅读

    相关 使用DeferredResult异步处理请求

         我们在实际开发的过程中,可能会遇到需要长轮循的情况。      长轮询指的是客户端向服务端发送一个请求,然后服务端将该请求hold住,另起线程进行处理,处理完后,再