日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

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

發布時間:2025/3/11 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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簡單的異步調用,以及部分源碼的解讀,有問題請各位社區大佬指教!

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的SpringMvc异步请求的使用及部分原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。