SpringMvc异步请求的使用及部分原理

最近隔壁项目组的项目又出问题了,一直被用户投诉太卡了,页面白屏的那种,打开源代码一看,全是非异步请求,类似于以下写法:

	@ResponseBody@RequestMapping(value = "/getTest")public String getTest() {System.out.println("主线程"+Thread.currentThread().getName()+"=>"+System.currentTimeMillis());try {Thread.sleep(8000);//模拟业务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主线程"+Thread.currentThread().getName()+"=>"+System.currentTimeMillis());return "success...";}

对于异步请求,用这个的好处呢是可以增大项目吞吐量,一个请求过来,将处理业务内容交于另外一个线程去执行,并且立即释放主线程,请求少的时候其客户端并感受不到,当请求多的时候,tomcat线程不够用时,会有部分用户客户端出线等待或白屏状态,体验不佳,增加tomcat线程也可以,但是tomcat线程数和机器性能参数有关,极限一般是在3000~5000左右不等,而且线程越多,CPU响应时间也长,请求线程响应时间也会过长,所以,设置tomcat线程数最好是找到一个平衡点

官网介绍:https://docs.spring.io/spring/docs/4.3.12.RELEASE/spring-framework-reference/html/mvc.html#mvc-ann-async

 从Servlet3.0和SpringMvc3.2以后开始支持异步请求,可以通过使用Callable这个回调接口实现,也可以通过DeferredResult这个对象进行实现,下面为具体官方介绍的用法,以下为两种用法,还有一种是使用WebAsyncTask

@PostMapping
public Callable<String> processUpload(final MultipartFile file) {return new Callable<String>() {public String call() throws Exception {// ...return "someView";}};}@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {DeferredResult<String> deferredResult = new DeferredResult<String>();// Save the deferredResult somewhere..return deferredResult;
}// In some other thread...
deferredResult.setResult(data);
    @RequestMapping("/getWebAsyncTask")@ResponseBodypublic WebAsyncTask<String> asyncTask(){System.out.println("主线程"+Thread.currentThread().getName()+"=>"+System.currentTimeMillis());// 1000 为超时设置,默认执行时间为10秒WebAsyncTask<String> webAsyncTask = new WebAsyncTask<String>(2000L,new Callable<String>(){public String call() throws Exception {System.out.println(Thread.currentThread().getName());//业务逻辑处理Thread.sleep(3000);System.out.println(Thread.currentThread().getName());return "WebAsyncTask success..";}});webAsyncTask.onCompletion(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName()+"调用完成");}});webAsyncTask.onTimeout(new Callable<String>() {public String call() throws Exception {System.out.println(Thread.currentThread().getName()+"业务处理超时");return "<h1>Time Out</h1>";}});System.out.println("主线程"+Thread.currentThread().getName()+"=>"+System.currentTimeMillis());return webAsyncTask;}

在异步请求的源码中的注释看到,在用异步的请求之前了都需要在web.xml加上的Servlet上面加上<async-supported>true</async-supported>

 

如果不加上会报以下错误(不过在使用SpringBoot项目的时候,这个会Spring被默认设置成true,所以在SpringBoot项目中无需设置):

严重: Servlet.service() for servlet [Main] in context with path [/TestWebMvc] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml.] with root cause
java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml.at org.springframework.util.Assert.state(Assert.java:392)at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.startAsync(StandardServletAsyncWebRequest.java:103)at org.springframework.web.context.request.async.WebAsyncManager.startAsyncProcessing(WebAsyncManager.java:428)at org.springframework.web.context.request.async.WebAsyncManager.startCallableProcessing(WebAsyncManager.java:308)at org.springframework.web.context.request.async.WebAsyncManager.startCallableProcessing(WebAsyncManager.java:255)

 进入到源码里面可以看到,报错的地方是在StandardServletAsyncWebRequest.java:103

其实就是直接的看到getRequest()这个对象的一个属性而已(request对象地址:org.apache.catalina.connector.Request@79b39c31,说明这个请求是tomcat中的一个对象):

在tomcat源码中,该对象的属性默认为false:

在公司的加上那个属性标签后,结果发现属性被公司的破平台jar包吃掉了,这时不慌,可以先设置一个拦截器或者过滤器,在这个里面加上一个属性,这样也可以设置异步属性:

request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

结果发现还是不行,这时逼我改源码呀!最后将StandardServletAsyncWebRequest.java这个类重写了一下,在判断之前设置一下请求属性,这样才好,现在回到异步使用那里,其实在官方还是一种异步的方法,使用WebAsyncTask这个类这个可以增加超时回调结果,在调用中,我们打印一下异步线程名称:

运行时时候会提示你请配置一个线程池,并且采用的线程为:MvcAsync,这个是SimpleAsyncTaskExecutor线程,但这个并非线程池,打开这个源码看的时候发现,他就是创建了一个新的Thread用来执行异步线程:

	/*** Template method for the actual execution of a task.* <p>The default implementation creates a new Thread and starts it.* @param task the Runnable to execute* @see #setThreadFactory* @see #createThread* @see java.lang.Thread#start()*/protected void doExecute(Runnable task) {Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));thread.start();}

那我们就先配置一个线程池:创建一个类继承WebMvcConfigurer接口,实现configureAsyncSupport方法:

现在就可以正常使用异步线程啦!

说一下MVC异步走向原理:

在上面那个执行截图来看,会发现在执行异步是,会多调用一次拦截器的preHandle方法:

其实就是,请求过来时,经过拦截器后发现该请求为异步,会将tomcat中的Servlet以及Filter退出容器,保持一个response的响应连接,当业务执行完毕后,会自动去请求一次容器,将结果返回到客户端上。

而且异步执行时,SpringMVC会先调用自己的前置处理器,在源码的WebAsyncManager.java类中:

三种前置处理器分别对应三种使用方式,其实使用Callable异步运行和使用WebAsyncTask在源码中是一致的,而且异步调用的源代码也是使用Future<?>这个类执行的(这里用到了并发这一块)保证执行的效率:

好了,这就是SpringMVC简单的异步调用,以及部分源码的解读,有问题请各位社区大佬指教!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Microsoft BizTalk ESB Toolkit 2.0

[>>> 更多<BizTalk开发系列>文章 ] 微软于6月8号发布了BizTalk Server 2009企业集成平台的最后一个功能组件:ESB Toolkit 2.0 (原名:ESB Guidance 2.0)&#xff0c;ESB ToolKit 2.0一个是工具和代码集扩展了BizTalk Server 2009对于松耦合和动态消息架构的支持…

python解释器环境中用于表示上一次运算结果的特殊变量_判断正误 PUSH CL_学小易找答案...

【单选题】将数学关系式2 【填空题】请用4位十六进制写出每条指令结束后AX的值。 MOV AX, 0 DEC AX ADD AX, 7FFFH ADC AX, 1 NEG AX OR AX, 3FDFH AND AX, 0EBEDH XCHG AH, AL SAL AX, 1 RCL AX, 1 【判断题】判断正误 MOV DX, 09H 【判断题】判断正误 MOV [1200H], [SI] 【单…

Java线程的使用及共享协作

创建线程的三种方式 1、继承Thread&#xff1b; static class MyThread extends Thread{Overridepublic void run() {//do something...} } public static void main(String[] args) throws InterruptedException {MyThread thread new MyThread ();thread.start(); } 2、实…

WCF学习笔记(三):开启net.tcp端口

正在做一个使用tcp协议的WCF示例&#xff0c;遇到很多问题。首当其冲的问题就是——如何为WCF打开tcp端口。。。 具体步骤如下&#xff1a; 1、在IIS中为WCF安装支持TCP协议的组件&#xff1a; 2、在防火墙的入栈规则中开启808端口&#xff1b; 3、在servies.msc中打开两个服务…

孪生神经网络_轩辕实验室:数字孪生:基于机器学习的汽车数字孪生模型

本文来源&#xff1a;A. Rassolkin, T. Vaimann, A. Kallaste, and V. Kuts, “Digital twin for propulsion drive of autonomous electric vehicle,” in 2019 IEEE 60th International Scientific Conference on Power and Electrical Engineering of Riga Technical Univer…

Java线程Fork/Join思想及实现

最近在看线程这一块的东西&#xff0c;所以之前的那篇文章就是用来记录的&#xff0c;但看起来好简单的样子&#xff0c;哈哈哈&#xff01; 这两天看的是Fork/Join 分而治之的思想&#xff0c;Doug Lea大师的JUC还是挺强的&#xff0c;学并发编程应该没有人不知道这个大佬吧&…

Sgen.exe: Speed up XmlSerializer's Startup Performance [.NET 2.0, XML Serialization]

Sgen.exe: Speed up XmlSerializers Startup Performance [.NET 2.0, XML Serialization] Written by Allen Lee 1. Why Sgen.exe? 在《Serialize Your Deck with Positron [XML Serialization, XSD, C#]》一文中&#xff0c;我们领略到 XML Serialization 是如何简化我们的 X…

Java线程并发常用工具类使用

这次整理了一些比较常用的线程工具类啦。 CountDownLatch&#xff1a;在一组线程执行完后&#xff0c;才能开始执行调用等待的线程。上片文章提到过junit的测试尽量不要测试线程&#xff0c;如果硬是要可以使用CountDownLatch进行测试 CyclicBarrier&#xff1a;在一组线程中…

三维图形几何变换算法实验_计算机视觉方向简介 | 深度学习视觉三维重建

点击上方“计算机视觉life”&#xff0c;选择“星标”快速获得最新干货作者&#xff1a; Moonsmilehttps://zhuanlan.zhihu.com/p/79628068本文已由作者授权&#xff0c;未经允许&#xff0c;不得二次转载三维重建意义三维重建作为环境感知的关键技术之一&#xff0c;可用于自动…

读《高效程序员的45个习惯——敏捷开发修炼之道》

本书主要用平易的语言讲述了45个有助于提高程序员自身敏捷的习惯&#xff0c;个人感觉这种老外写的书翻译成中文就少了很多意思。 主要的45个习惯是&#xff1a; 做事欲速则不达对事不对人排除万难跟踪变化对团队投资懂得丢弃打破沙锅问到底把握开发节奏让客户做决定让设计指导…

Java线程CAS原子操作

这次分享一些关于原子操作(CAS)的东西. 定义 CAS(Compare And Swap)是CPU的一个指令级别的操作&#xff0c;叫原子操作&#xff0c;原子操作是不可分割的&#xff0c;跟事务差不多&#xff0c;要么全部执行完成&#xff0c;要么不执行&#xff1b; 像这种操作有点类似阻塞锁…

python 导航栏_解析导航栏的url--selnium,beautifulsoup实战

前段时间做ui自动化测试的时候&#xff0c;导航栏菜单始终有点问题&#xff0c;最后只好直接获取到url&#xff0c;然后直接使用driver.get(url)进入页面&#xff1b;包括做压测的时候&#xff0c;比如我要找出所有报表菜单的url&#xff0c;这样不可能手动去一个一个找出来&am…

PNG图片详解

1、PNG图片类型 PNG格式有8位、24位、32位三种&#xff0c;下面是一些术语&#xff1a; 索引透明&#xff1a;类似于GIF&#xff0c;某一像素只有全透和全不透明两种效果Alpha透明&#xff1a;半透明PNG8 8位的PNG最多支持256&#xff08;2的8次方&#xff09;种颜色&#xff0…

Java并发编程之显式锁(Lock)使用

又是一个基于AQS好用的类&#xff0c;看来下次有必要看看AQS了&#xff0c;正好又是放假。 既然叫显式锁&#xff0c;必然也有隐式锁&#xff0c;也就是所谓的synchronzied关键字&#xff0c;它们两者的区别呢在于使用范围&#xff0c;synchronzied关键字的使用范围比Lock要小…

python pychart三维_详解python模块pychartdir安装及导入问题

在迁移别人写好的脚本时&#xff0c;发现pychartdir没有导入&#xff0c;脚本执行报错。以下是报错内容&#xff1a;[modpsLGJF-ZYC5-MMSC-WEB02 ~]$ python /opt/aspire/product/modps/mopps/shell/dayreport_linux.py/etc/host.conf: line 1: bad command nospoof on"Tr…

vim 中Ctags的安装和使用

Ctags是一个用来为源文件中的标识符&#xff08;如变量、函数、类成员、宏定义等&#xff09;创建索引文件的程序。这些tags文件能被编辑器或其它工具用来快速查找定位源代码中的符号&#xff08;tag/symbol&#xff09;&#xff0c;如变量名&#xff0c;函数名等。 Tags文件中…

Java并发编程之AbstractQueuedSynchronizer(AQS)源码解析

自己一个人随便看看源码学习的心得&#xff0c;分享一下啦&#xff0c;不过我觉得还是建议去买本Java并发编程的书来看会比较好点&#xff0c;毕竟个人的理解有限嘛。 独占锁和共享锁 首先先引入这两个锁的概念&#xff1a;独占锁即同一时刻只有一个线程才能获取到锁&#xf…

采集用python还是火车头_我才知道爬虫也可以酱紫--火车采集器

我才知道爬虫还可以这样—火车采集器的使用说在前面额。。。好吧&#xff0c;我这一个三毛钱的屌丝也开始步入实习阶段了&#xff0c;在北京其实也挺好的&#xff0c;虽说压力大&#xff0c;但是今后就业机会也相对而言大一些。好了&#xff0c;说回今天的主题&#xff0c;之前…

mvn 使用中的错误

出现这种错误的时候&#xff1a;mvn Error building POM may not be this projects POM&#xff0c;报的是那个jar 包&#xff0c;就删除那个jar 包&#xff0c;重新mvn clean install .ok

Java并发编程之FutureTask源码解析

上次总结一下AQS的一些相关知识&#xff0c;这次总结了一下FutureTask的东西&#xff0c;相对于AQS来说简单好多呀 之前提到过一个LockSupport的工具类&#xff0c;也了解一下这个工具类的用法&#xff0c;这里也巩固一下吧 /*** Makes available the permit for the given th…