哈尔滨城乡建设网站,网站首眉怎么做,互联网大厂设计哪家口碑好,免费建立自己的网站空间最近隔壁项目组的项目又出问题了#xff0c;一直被用户投诉太卡了#xff0c;页面白屏的那种#xff0c;打开源代码一看#xff0c;全是非异步请求#xff0c;类似于以下写法#xff1a; ResponseBodyRequestMapping(value /getTest)public String getTest(…最近隔壁项目组的项目又出问题了一直被用户投诉太卡了页面白屏的那种打开源代码一看全是非异步请求类似于以下写法 ResponseBodyRequestMapping(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 CallableString processUpload(final MultipartFile file) {return new CallableString() {public String call() throws Exception {// ...return someView;}};}RequestMapping(/quotes)
ResponseBody
public DeferredResultString quotes() {DeferredResultString deferredResult new DeferredResultString();// Save the deferredResult somewhere..return deferredResult;
}// In some other thread...
deferredResult.setResult(data); RequestMapping(/getWebAsyncTask)ResponseBodypublic WebAsyncTaskString asyncTask(){System.out.println(主线程Thread.currentThread().getName()System.currentTimeMillis());// 1000 为超时设置默认执行时间为10秒WebAsyncTaskString webAsyncTask new WebAsyncTaskString(2000L,new CallableString(){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 CallableString() {public String call() throws Exception {System.out.println(Thread.currentThread().getName()业务处理超时);return h1Time Out/h1;}});System.out.println(主线程Thread.currentThread().getName()System.currentTimeMillis());return webAsyncTask;}
在异步请求的源码中的注释看到在用异步的请求之前了都需要在web.xml加上的Servlet上面加上async-supportedtrue/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-supportedtrue/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-supportedtrue/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.Request79b39c31说明这个请求是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.* pThe 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简单的异步调用以及部分源码的解读有问题请各位社区大佬指教