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

 @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简单的异步调用,以及部分源码的解读,有问题请各位社区大佬指教!

SpringMvc异步请求的使用及部分原理相关推荐

  1. springMVC获取异步请求的参数,返回异步请求数据(json),跨域访问简单了解,文件上传,与Restful风格

    springMVC获取异步请求的参数 JQuery发送异步请求回顾 <a href="javascript:void(0);" id="testAjax" ...

  2. SpringMVC中的异步请求-跨域访问

    发送异步请求: <%@page pageEncoding="UTF-8" language="java" contentType="text/h ...

  3. Ajax异步请求原理和过程

    Ajax异步请求原理和过程 1.什么是Ajax 2.AJAX创建异步对象XMLHttpRequest ( 考虑兼容性 ) 3.操作XMLHttpRequest 对象 1.什么是Ajax Ajax是一种 ...

  4. 内存泄漏的3个解决方案与原理实现,掌握一个轻松应对开发丨内存池|mtrace||API实现|框架封装|中间件|异步请求|连接池

    内存泄漏的3个解决方案与原理实现,掌握一个轻松应对开发 视频讲解如下,点击观看: 内存泄漏的3个解决方案与原理实现,掌握一个轻松应对开发丨内存池|mtrace||API实现|框架封装|中间件|异步请求 ...

  5. Ajax异步请求原理

    一.Ajax能做什么 异步请求.局部刷新 二.同步请求 JavaScript的特点是单线程,也正是因为单线程造成了同步请求 1. 为什么JS是单线程: JS 作为游览器脚本语言,主要用途是和用户交互. ...

  6. 异步请求-XMLHttpRequest、ajax、axios原理浅析

    异步请求-XMLHttpRequest.ajax.axios原理浅析 XMLHTTPRequest 参考 readyState responseType XMLHttpRequest Level 1 ...

  7. DNS异步请求池原理与实现

    在阅读本文之前,需要熟悉DNS协议,请参考:https://blog.csdn.net/xianjian1990/article/details/104182896 文章目录 同步与异步的区别 为什么 ...

  8. SpringMVC @RequestBody和@ResponseBody注解 以及 Ajax异步请求

    实例讲解: 1.先创建一个实体类Student: public class Student {private String sname;private String password;private ...

  9. java异步请求显示数据格式_JSON(四)——异步请求中前后端使用Json格式的数据进行交互...

    json格式的数据广泛应用于异步请求中前后端的数据交互,本文主要介绍几种使用场景和使用方法. 一,json格式字符串 functionsentAjax(){ $.ajax({ type:'POST', ...

最新文章

  1. 数据库内核月报 - 2015 / 11-MySQL · 社区见闻 · OOW 2015 总结 MySQL 篇
  2. usb大容量存储设备驱动_Win10默认已禁用USB驱动器缓存 1903版本起无需点击安全弹出...
  3. 批处理-取年月日、时分秒毫秒
  4. 不同平台上安装python的方式是一样的对还是错_不可以在同一台计算机上安装多个不同的Python版本...
  5. Android之mvp和mvc对比分析以及实际应用
  6. 【Qt】Qt之进程间通信(共享内存)【转】
  7. assets和res/raw的用法
  8. 帝国cms怎么搭建python环境_用python 发 帝国cms 文章
  9. Commons-VFS 使用SFTP
  10. 面试、笔试中常用的SQL语句(数据库知识必杀)一共50个!!!
  11. 遗传算法解决作业调度c语言,应用遗传算法解决车间作业调度问题
  12. HTML5游戏设计与制作
  13. 小米笔记本怎么恢复出厂的系统
  14. 雄迈摄像头ffmpeg转码推送至websocket
  15. 服务器存档修改器,太吾绘卷存档修改器v2.6
  16. CryEngine架构概览
  17. android手机电池寿命,真正有效延长手机电池寿命的几点建议(iPhone+安卓)
  18. opencv应用实例-金圣韬-专题视频课程
  19. 推荐一个视频播放器potplayer
  20. 括弧匹配检验(括号匹配问题)

热门文章

  1. 服务器LIMIT是什么信号,Postfix添加milter-limit配置方案
  2. 从网络访问计算机不能添加用户名,windows7 和 XP 能互相用\\ip访问,但是不能通过计算机名访问...
  3. edges2shoes数据集下载_edges2cats
  4. dw连接服务器文档类型,dw怎么连接服务器的数据库
  5. lbp matlab程序,求教大神给解释一下LBP特征提取每部分程序是干什么的
  6. C语言从来都没有过时,你大爷终究是你大爷
  7. c语言宏定义比较三个数大小,C语言中两个宏进行大小对比,其中一个没有定义,这种行为如何定义。...
  8. jq输出文本_如何用 Linux 命令行工具解析和格式化输出 JSON | Linux 中国
  9. python爬取微博内容_请问该如何通过python调用新浪微博的API来爬取数据?
  10. 苹果6怎样打开html,苹果iPhone的Safari浏览器使用技巧图解