前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMVC添加异步请求支持

SpringMVC添加异步请求支持

作者头像
Meet相识
发布2018-09-12 16:17:51
2.5K0
发布2018-09-12 16:17:51
举报
文章被收录于专栏:技术专栏
  • WebAsyncTask
使用场景:由于java web项目主线程可以处理的线程数有限,当请求量过大,主线程就会阻塞,所以需要后台接收到请求的时候需要启动副线程去完成业务逻辑的处理,主线程直接返回,这样主线程占用的时间很短,可以继续处理下一个请求

使用场景

使用方法:将SpringMVC的controller的方法的返回值封装成WebAsyncTask,并设置超时时间,和超时处理方法
使用案例
代码语言:javascript
复制
@RequestMapping(value = "/cheStopWithoutAuth")
    @ResponseBody
    public WebAsyncTask<AjaxResponse> cheStop(String port,
                                          AjaxResponse ar) throws Exception {
        log.info("主线程开始");
        Callable<AjaxResponse> result = (() -> {
            log.info("副线程开始");
            cheService.cheStop(port);
            ar.setSuccessMessage(ResultEnums.SUCCESS.getMessage(), null);
            log.info("副线程结束");
            return ar;
        });  //直接Callable也可以,但是不能设置超时时间和超时回调以及成功回调
        WebAsyncTask<AjaxResponse> webAsyncTask = new WebAsyncTask<>(300000, result);
        webAsyncTask.onTimeout(timeOutCallBack());
        log.info("主线程结束");
        return webAsyncTask;
    }

/**
* 超时回调
*/
private Callable<AjaxResponse> timeOutCallBack(){

        Callable<AjaxResponse> callback = (()-> {
                log.info("请求超时");
                AjaxResponse ajaxResponse = new AjaxResponse();
                ajaxResponse.setErrorMessage("请求超时",null);
                return ajaxResponse;
        });
        return callback;
    }

注意web.xml应用需在所有的servlet和filter配置加上<async-supported>true</async-supported>

filter放置位置

servlet放置位置

  • DeferredResult
使用场景:当遇到主线程直接启动副线程去处理请求逻辑的时候,WebAsyncTask可以满足,但是如果遇到一些比较复杂的情况,比如当前web应用接收到下单请求会把请求放在一个异步处理消息队列里,然后由应用2去处理队列里的请求,应用2处理完把结果返回应用1,然后应用1监听消息队列的返回结果并返回给前端,这个时候WebAsyncTask就无能为力了,这时可以使用DeferredResult

使用场景

使用方法:每次接收到请求,主线程即把请求放在消息队列中,然后创建一个DeferredResult,并将请求的唯一键与创建的DeferredResult封装到异步请求处理器里,然后注册监听器,监听消息队列的完成事件,当监听到完成事件事,从map中取出对应的DeferredResult并返回
使用案例
1、模拟消息队列
代码语言:javascript
复制
@Data
@Slf4j
@Component
public class MockQueue {
    private String placeOrder;   //下单消息
    private String completeOrder;  //订单完成的消息

    public void setPlaceOrder(String placeOrder) throws InterruptedException {
        new Thread(()->{
            log.info("接单下单请求");
            try {
                Thread.sleep(1000);  //模拟应用2处理下单的过程
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.completeOrder = placeOrder;  //给completeOrder设值代表下单请求处理完毕
            log.info("下单请求处理完毕:{}",placeOrder);
        }).start();

    }

    public void setCompleteOrder(String completeOrder) {
        this.completeOrder = completeOrder;
    }
}
2、模拟异步请求处理器
代码语言:javascript
复制
@Component
@Data
public class DeferredResultHolder {

    //key:订单号  value:订单的处理结果
    private Map<String,DeferredResult<String>> map = new HashMap<>();
}
3、注册监听器监听请求处理完成事件
代码语言:javascript
复制
/**
 * ContextRefreshedEvent 整个容器初始化完毕的一个事件
 */
@Component
@Slf4j
public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
    @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.getCompleteOrder();
                    log.info("返回订单处理结果:"+orderNumber);
                    deferredResultHolder.getMap().get(orderNumber).setResult("place order success");
                    mockQueue.setCompleteOrder(null);
                }else{
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}
4、主线程组织调用
代码语言:javascript
复制
 @RequestMapping("/order2")
    public DeferredResult<String> order2() throws InterruptedException {
        log.info("主线程开始");
        String orderNumber = RandomStringUtils.randomNumeric(8);  //生成8位订单号

        mockQueue.setPlaceOrder(orderNumber);

        DeferredResult<String> result = new DeferredResult<>();
        deferredResultHolder.getMap().put(orderNumber,result);

        log.info("主线程返回");
        return result;
    }

注意SprinbBoot中对异步请求注册过滤器应继承WebMvcConfigurerAdapter并重写configureAsyncSupport方法

image.png

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.10.10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用场景:由于java web项目主线程可以处理的线程数有限,当请求量过大,主线程就会阻塞,所以需要后台接收到请求的时候需要启动副线程去完成业务逻辑的处理,主线程直接返回,这样主线程占用的时间很短,可以继续处理下一个请求
  • 使用方法:将SpringMVC的controller的方法的返回值封装成WebAsyncTask,并设置超时时间,和超时处理方法
  • 使用案例
  • 使用场景:当遇到主线程直接启动副线程去处理请求逻辑的时候,WebAsyncTask可以满足,但是如果遇到一些比较复杂的情况,比如当前web应用接收到下单请求会把请求放在一个异步处理消息队列里,然后由应用2去处理队列里的请求,应用2处理完把结果返回应用1,然后应用1监听消息队列的返回结果并返回给前端,这个时候WebAsyncTask就无能为力了,这时可以使用DeferredResult
  • 使用方法:每次接收到请求,主线程即把请求放在消息队列中,然后创建一个DeferredResult,并将请求的唯一键与创建的DeferredResult封装到异步请求处理器里,然后注册监听器,监听消息队列的完成事件,当监听到完成事件事,从map中取出对应的DeferredResult并返回
  • 使用案例
    • 1、模拟消息队列
      • 2、模拟异步请求处理器
        • 3、注册监听器监听请求处理完成事件
          • 4、主线程组织调用
          相关产品与服务
          消息队列 CMQ
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档