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.pswp.cn/news/537189.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537189.shtml
英文地址,請注明出處:http://en.pswp.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…