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对于松耦合和动态消息架构的支持…

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…

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

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

PNG图片详解

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

java 删除二维数组中的null_避免在Java中检查Null语句

1.概述通常&#xff0c;在Java代码中处理null变量、引用和集合很棘手。它们不仅难以识别&#xff0c;而且处理起来也很复杂。事实上&#xff0c;在编译时无法识别处理null的任何错误&#xff0c;会导致运行时NullPointerException。在本教程中&#xff0c;我们将了解在Java中检…

simulink显示多个数据_如何在 Simulink 中使用 PID Tuner 进行 PID 调参?

作者 | 安布奇责编 | 胡雪蕊出品 | CSDN(ID: CSDNnews)本文为一篇技术干货&#xff0c;主要讲述在Simulink如何使用PID Tuner进行PID调参。PID调参器( PIDTuner)概述1.1 简介使用PID Tuner可以对Simulink模型中的PID控制器&#xff0c;离散PID控制器&#xff0c;两自由度PID控制…

Java并发编程之堵塞队列介绍以及SkipList(跳表)

堵塞队列 先了解一下生产者消费者模式&#xff1a; 生产者就是生产数据的一方&#xff0c;消费者就是消费数据的另一方。在多线程开发中&#xff0c;如果生产者处理速度很快&#xff0c;而消费者处理速度很慢&#xff0c;那么生产者就必须等待消费者处理完&#xff0c;才能继…

Java并发编程之线程池ThreadPoolExecutor解析

线程池存在的意义 平常使用线程即new Thread()然后调用start()方法去启动这个线程&#xff0c;但是在频繁的业务情况下如果在生产环境大量的创建Thread对象是则会浪费资源&#xff0c;不仅增加GC回收压力&#xff0c;并且还浪费了时间&#xff0c;创建线程是需要花时间的&…

Java并发编程之线程定时器ScheduledThreadPoolExecutor解析

定时器 就是需要周期性的执行任务&#xff0c;也叫调度任务&#xff0c;在JDK中有个类Timer是支持周期性执行&#xff0c;但是这个类不建议使用了。 ScheduledThreadPoolExecutor 继承自ThreadPoolExecutor线程池&#xff0c;在Executors默认创建了两种&#xff1a; newSin…

Spring中BeanFactory和FactoryBean的区别

先介绍一下Spring的IOC容器到底是个什么东西&#xff0c;都说是一个控制反转的容器&#xff0c;将对象的控制权交给IOC容器&#xff0c;其实在看了源代码之后&#xff0c;就会发现IOC容器只是一个存储单例的一个ConcurrentHashMap<String, BeanDefinition> BeanDefiniti…

Spring中Aware的用法以及实现

Aware 在Spring当中有一些内置的对象是未开放给我们使用的&#xff0c;例如Spring的上下文ApplicationContext、环境属性Environment&#xff0c;BeanFactory等等其他的一些内置对象&#xff0c;而在我们可以通过实现对应的Aware接口去拿到我们想要的一些属性&#xff0c;一般…

Spring Bean的生命周期以及IOC源码解析

IOC源码这一块太多只能讲个大概吧&#xff0c;建议还是去买本Spring IOC源码解析的书来看比较好&#xff0c;我也是自己看源代码以及视频整理的笔记 Bean的生命周期大概可以分为四个阶段&#xff0c;具体的等会再说&#xff0c;先看看IOC的源码吧 1、bean的创建 2、bean的属…

MongoDB位运算基本使用以及位运算应用场景

最近在公司业务上用到了二进制匹配数据&#xff0c;但是MongoDB进行二进制运算&#xff08;Bitwise&#xff09;没用过&#xff0c;网上博客文章少&#xff0c;所以就上官网看API&#xff0c;因此记录一下&#xff0c;顺便在普及一下使用二进制位运算的一些应用。 在MongoDB的…

Mybatis源码日志模块分析

看源码需要先下载源码&#xff0c;可以去Mybatis的github上的仓库进行下载&#xff0c;Mybatis 这次就先整理一下日志这一块的源码分析&#xff0c;这块相对来说比较简单而且这个模块是Mybatis的基础模块。 之前的文章有谈到过Java的日志实现&#xff0c;大家也可以参考一下&…

python手机端给电脑端发送数据_期货交易软件有哪些比较好用?分手机端和电脑端...

一、电脑端交易软件期货电脑端交易软件目前市场上用的最多的是文华财经和博易大师&#xff0c;这两个软件都是免费交易使用的。从投资者使用角度来看&#xff0c;目前电脑端文华财经的评价比博易大师高一些。当然每个投资者有自己的使用习惯&#xff0c;博易大师也有自己优点&a…

Find the Difference(leetcode389)

2019独角兽企业重金招聘Python工程师标准>>> Given two strings s and t which consist of only lowercase letters. String t is generated by random shuffling string s and then add one more letter at a random position. Find the letter that was added in …