Java SpringMVC异步处理详解
作者:路人甲Java 发布时间:2021-08-10 15:03:58
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、总结
当接口中有大量的耗时的操作,且这些耗时的操作让线程处于等待状态时,此时为了提升系统的性能,可以将接口调整为异步处理的方式。
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
来源:https://blog.csdn.net/likun557/article/details/119066603
猜你喜欢
- 本文实例讲述了Java使用JDBC连接postgresql数据库。分享给大家供大家参考,具体如下:package tool;import j
- java 中遍历取值异常(Hashtable Enumerator)解决办法用迭代器取值时抛出的异常:java.util.NoSuchEle
- 一、实验目的1. 掌握输入输出流的总体结构;2. 掌握流的概念;3. 掌握FileInputStream类、FileOutputStream
- 页面:上传文件时的关键词:enctype="multipart/form-data"<%@ page langua
- 使用场景EntityListeners在jpa中使用,如果你是mybatis是不可以用的它的意义对实体属性变化的跟踪,它提供了保存前,保存后
- 前言我们在 页面切换转场动画,英雄救场更有趣!介绍了 Hero 动画效果,使用 Hero 用于转场能够提供非常不错的体验。既然称之
- 最近看spring的JDBCTemplete的模板方式调用时,对模板和回调产生了浓厚兴趣,查询了一些资料,做一些总结。回调函数:所谓回调,就
- Java中线程分为两种类型:用户线程和守护(服务)线程。通过Thread.setDaemon(false)设置为用户线程;通过Thread.
- 十六进制字符串与数值类型之间转换(C# 编程指南) 以下示例演示如何执行下列任务: 获取字符串中每个字符的十六进制值。 获取与十六进制字符串
- 本文实例为大家分享了C语言实现扫雷游戏的具体代码,供大家参考,具体内容如下前言一、游戏规则介绍扫雷是一个十分经典的游戏,一张棋盘中有很多个不
- iOS定位 - 普通定位(没有地图) - 反地理编码(得到具体位置),下面通过代码给大家详解,代码如下:#import <CoreLo
- 走马灯是一种常见的效果,本文讲一下如何用 PageView 在 Flutter 里实现一个走马灯, 效果如下,当前页面的高度比其它页面高,切
- 在IntelliJ IDEA 中这个查看一个类也就是当前类的所有继承关系,包括实现的所有的接口和继承的类,这个继承,不仅仅是一级的继承关系,
- java web返回中文乱码ajax返回中文乱码问题 在浏览器按F12查看数据包可以看到charset为 iso-8859-1,这是spri
- 一.关于使用Mybatisplus自带的selectById和insert方法时的一些问题1.selectById的问题(1).表的主键列名
- SSM Mapper查询出返回数据查不到个别字段原因开启了驼峰命名法则,Bean里的字段不识别_注释掉或者把实体类里的字段_去掉换位大写SS
- 在项目中,需要使用XStream将xml string转成相应的对象,却报出了java.lang.ClassCastException: c
- 引言ShardingSphere的SQL解析,本篇文章源码基于4.0.1版本ShardingSphere的分片引擎从解析引擎到路由引擎到改写
- 在导入studio工程的时候,进行sync的时候,提示Error:Configuration with name 'default&
- 实现要求1、使用Java图形界面组件设计软件,界面如图所示。2、软件能够满足基本的“加、减、乘、除"等运算要求。3、程序代码清晰,