Java SpringMVC异步处理详解

 更新时间:2021年10月26日 15:17:07   作者:路人甲Java  
这篇文章主要介绍了Java springmvc的处理异步,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

1、本篇内容

本文让大家掌握 springmvc 中异步处理请求,特别牛逼的一个功能,大家一定要掌握。

2、看段代码,分析问题

@ResponseBody
@RequestMapping("/async/m1.do")
public String m1() throws InterruptedException {
    long st = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",开始");
    //休眠3秒,模拟耗时的业务操作
    TimeUnit.SECONDS.sleep(3);
    long et = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",结束,耗时(ms):" + (et - st));
    return "ok";
}

这段代码很简单

这段代码是 springmvc 提供的一个接口

内部休眠了 3 秒钟,用来模拟耗时的操作

方法内部有 2 条日志(日志中包含了当前线程、开始时间、结束时间、耗时)

浏览器中访问下这个接口,效果如下,可以看到接口耗时 3s 左右。

控制台输出

主线程:Thread[http-nio-8080-exec-1,5,main],1624889293055,开始
主线程:Thread[http-nio-8080-exec-1,5,main],1624889293055,结束,耗时(ms):3002

从输出中,我们可以看出,这个接口从开始到结束都是由 tomcat 中的线程来处理用户请求的,也就是说,3 秒这段时间内,tomcat 中的一个线程会被当前请求一直占用了则,tomcat 线程是有最大值的,默认情况下好像是 75,那么问题来了。

当 3 秒之内,来的请求数量超过了 tomcat 最大线程数的时候,其他请求就无法处理了,而此时 tomcat 中这些线程都处理 sleep 3s 的休眠状态,cpu 此时没活干,此时就会造成机器没活干,但是呢又不能处理新的请求,这就是坑啊,浪费资源,怎么办呢?

遇到这种场景的,也就是说接口内部比价耗时,但是又不能充分利用 cpu 的,我们可以采用异步的方式来处理请求,过程如下:

tomcat 线程,将请求转发给我们自定义的子线程去处理这个请求,然后 tomcat 就可以继续去接受新的请求了。

3、springmvc 中异步处理

主要有 3 个大的步骤。

step1:servlet 开启异步处理支持

web.xml 中开启 servlet 异步支持

step2:Filter 中添加异步支持

如果我们的异步请求需要经过 Filter 的,那么需要在 web.xml 对这个 Filter 添加异步支持.

step3:接口返回值为 DeferredResult

这个步骤中细节比较多,当需要异步响应请求的时候,返回值需要为 DeferredResult,具体参考下面案例代码,详细信息都在注释中了,大家注意看注释。

第 1 步:创建 DeferredResult<返回值类型>(超时时间[毫秒],超时回调的代码)

第 2 步:在子线程中异步处理业务,调用 DeferredResult 的 setResult 方法,设置最终返回到客户端的结果,此方法调用以后,客户端将接收到返回值,然后响应过程请求就结束了

第 3 步:将 DefaultResult 作为方法返回值

/**
 * 使用springmvc的异步功能,业务处理放在异步线程中执行
 *
 * @param timeout 异步处理超时时间(毫秒)
 * @return
 */
@ResponseBody
@RequestMapping("/async/m2/{timeout}.do")
public DeferredResult m2(@PathVariable("timeout") long timeout) {
    long st = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",开始");
    /**
     * 1、创建DeferredResult<返回值类型>(超时时间[毫秒],超时回调的代码)
     */
    DeferredResult result = new DeferredResult(timeout, () -> {
        System.out.println("超时了");
        return "timeout";
    });
    //2、异步处理业务,
    new Thread(() -> {
        //开启一个异步线程,在异步线程中进行业务处理操作
        try {
            TimeUnit.SECONDS.sleep(3);
            //3、调用DeferredResult的setResult方法,设置最终返回到客户端的结果,此方法调用以后,客户端将接收到返回值
            result.setResult("ok");
        } catch (InterruptedException e) {
            result.setResult("发生异常了:" + e.getMessage());
        }
    }).start();
    long et = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",结束,耗时(ms):" + (et - st));
    //3、将DefaultResult作为方法返回值
    return result;
}

上面的 m2 方法个 timeout 参数,调用者通过这个参数来指定接口的超时时间,未超时的情况下,也就是说 timeout 大于 3 秒的时候,此时会输出 ok,否则将出现超时,此时会将 DeferredResult 构造器第 2 个参数的执行结果作为最终的响应结果,即会向客户端输出 timeout。

使用建议:案例开启了一个新的子线程来执行业务操作,生产环境中,建议大家采用线程池的方式,效率更高。

下面我们来通过 2 个 case 来模拟下这个接口超时和正常的结果。

4、模拟非超时请求

当 timeout 大于 3 秒时,才不会出现超时,此时我们传递 4000 毫秒来试试

控制台输出如下,可以看到主线程瞬间就结束了。

主线程:Thread[http-nio-8080-exec-6,5,main],1624891886020,开始
主线程:Thread[http-nio-8080-exec-6,5,main],1624891886020,结束,耗时(ms):0

5、模拟超时请求

当 timeout 小于 3 秒会出现超时,此时我们传递 1000 毫秒来试试

控制台输出如下,输出了超时信息,且通过前两行输出看出主线程瞬间就结束了,不会被请求阻塞。

主线程:Thread[http-nio-8080-exec-1,5,main],1624892109695,开始
主线程:Thread[http-nio-8080-exec-1,5,main],1624892109695,结束,耗时(ms):0
超时了

6、总结

当接口中有大量的耗时的操作,且这些耗时的操作让线程处于等待状态时,此时为了提升系统的性能,可以将接口调整为异步处理的方式。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • 例题详解Java dfs与记忆化搜索和分治递归算法的使用

    例题详解Java dfs与记忆化搜索和分治递归算法的使用

    递归指函数调用自身。常用的递归算法有dfs(深度优先搜索)、记忆化搜索和分治,接下来将用几个算法题来带你熟练掌握它
    2022-04-04
  • Java核心教程之常见时间日期的处理方法

    Java核心教程之常见时间日期的处理方法

    这篇文章主要给大家介绍了关于Java核心教程之常见时间日期的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • 使用Spring MVC实现双向数据绑定

    使用Spring MVC实现双向数据绑定

    Spring MVC是一个广泛用于构建Java Web应用程序的框架,它提供了众多功能,包括双向数据绑定,在这篇文章中,我们将向Java新手介绍如何使用Spring MVC实现双向数据绑定,以及为什么这个特性如此重要,需要的朋友可以参考下
    2024-01-01
  • Spring Boot中防止递归查询的两种方式

    Spring Boot中防止递归查询的两种方式

    这篇文章主要给大家介绍了关于Spring Boot中防止递归查询的两种方式,两种方式分别是在application.properties中配置和在entity中添加注解,都给出了详细的示例代码,需要的朋友们下面来一起看看吧。
    2017-06-06
  • MyBatisPlus标准数据层CRUD的使用详解

    MyBatisPlus标准数据层CRUD的使用详解

    这篇文章主要介绍了MyBatisPlus标准数据层CRUD的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Spring-MVC异步请求之Servlet异步处理

    Spring-MVC异步请求之Servlet异步处理

    这篇文章主要介绍了Spring-MVC异步请求之Servlet异步处理,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • 详解在SpringBoot中使用MongoDb做单元测试的代码

    详解在SpringBoot中使用MongoDb做单元测试的代码

    这篇文章主要介绍了详解在SpringBoot中使用MongoDb做单元测试的代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Java主流压缩解压工具对比、用法与选取详解

    Java主流压缩解压工具对比、用法与选取详解

    开发过程中可能会用到压缩文件的需求,下面这篇文章主要给大家介绍了关于Java主流压缩解压工具对比、用法与选取的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • MyBatis-Plus实现条件查询的三种格式例举详解

    MyBatis-Plus实现条件查询的三种格式例举详解

    本文主要介绍了MyBatis-Plus三中条件查询格式的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • java Future 接口使用方法详解

    java Future 接口使用方法详解

    这篇文章主要介绍了java Future 接口使用方法详解,Future接口是Java线程Future模式的实现,可以来进行异步计算的相关资料,需要的朋友可以参考下
    2017-03-03

最新评论