springboot如何實(shí)現(xiàn)異步響應(yīng)請(qǐng)求(前端請(qǐng)求超時(shí)的問(wèn)題解決)
問(wèn)題
實(shí)際場(chǎng)景中會(huì)遇到請(qǐng)求業(yè)務(wù)處理流程耗時(shí)較長(zhǎng),比如長(zhǎng)查詢,遠(yuǎn)程調(diào)用等,主線程會(huì)被一直占用會(huì)影響其他請(qǐng)求的響應(yīng),導(dǎo)致服務(wù)端性能下降。同時(shí),前端向服務(wù)端發(fā)送請(qǐng)求后等待響應(yīng)的超時(shí)時(shí)間比較短(一般20s或30s),而我們實(shí)際業(yè)務(wù)執(zhí)行可能超過(guò)1分鐘。所以以下需要解決此問(wèn)題。
解決方案
解決的方案分為以下兩步驟
1.服務(wù)端異步處理
你需要將請(qǐng)求接口進(jìn)行一些簡(jiǎn)單的改進(jìn)。將返回值類型變更為Callable<T>類型,其中T可以為任意類型比如String或你原有的自定義類型。以下的new Callable方法,會(huì)新建一個(gè)線程,用于執(zhí)行業(yè)務(wù)邏輯并在得到結(jié)果后自動(dòng)返回給前端。而主線程無(wú)需等待。
@RequestMapping(value = "/GetMonthCdateRpt", method = RequestMethod.POST) @ResponseBody public Callable<String> GetMonthCdateRpt(@RequestBody HashMap<String, Object> map) { logger.info("主線程開始"); Callable<String> result = new Callable<String>() { @Override public String call() throws Exception { logger.info("副線程開始"); /*這里沉睡1分鐘,表示處理耗時(shí)業(yè)務(wù)執(zhí)行*/ Thread.sleep(60000); logger.info("副線程返回"); return "success"; } }; logger.info("主線程返回"); return result; }
2.設(shè)置響應(yīng)時(shí)間
改進(jìn)完接口方法后,你需要新建一個(gè)WebMvcConfigurer的實(shí)現(xiàn)類,名字可以為WebAppConfigurer.java實(shí)現(xiàn)異步處理的支持,如果你已存在其他WebMvcConfigurer的類,則可以把代碼追加進(jìn)去。代碼內(nèi)容如下:
@Configuration public class WebAppConfigurer implements WebMvcConfigurer { //異步處理支持 @Override public void configureAsyncSupport(final AsyncSupportConfigurer configurer) { configurer.setDefaultTimeout(120 * 1000L);//設(shè)置響應(yīng)時(shí)間 120s configurer.registerCallableInterceptors(timeoutInterceptor()); configurer.setTaskExecutor(threadPoolTaskExecutor()); } @Bean public TimeoutCallableProcessingInterceptor timeoutInterceptor() { return new TimeoutCallableProcessingInterceptor(); } @Bean public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor t = new ThreadPoolTaskExecutor(); t.setCorePoolSize(10); t.setMaxPoolSize(100); t.setQueueCapacity(20); t.setThreadNamePrefix("WYF-Thread-"); return t; } }
執(zhí)行完以上兩步驟后,重新運(yùn)行服務(wù),調(diào)用接口可以異步返回結(jié)果,前端可以最大等待的響應(yīng)時(shí)間為上面設(shè)置的120s。
問(wèn)題已解決。
需要避免踩到的坑
如果你按上文操作后,還是會(huì)出現(xiàn)超時(shí)情況,有可能是你用到了以下幾種軟件,需要對(duì)應(yīng)設(shè)置一下。
1.關(guān)于dubbo中的設(shè)置
如果你使用的了dubbo這樣的Jave RPC框架,你除了以上設(shè)置后,還需要在application-*.yml配置消費(fèi)端的默認(rèn)的響應(yīng)超時(shí)時(shí)長(zhǎng)。
dubbo: application: name: myweb consumer: timeout: 120000 #默認(rèn)超時(shí)時(shí)間120s
同時(shí)注意在dubbo的*consume-client.xml服務(wù)注冊(cè)中,如果單個(gè)服務(wù)也設(shè)置了timeout會(huì)以各自服務(wù)配置的超時(shí)時(shí)長(zhǎng)為準(zhǔn)。如下的timeout是20秒,需要修改或直接刪掉此timeout的設(shè)置。
<dubbo:reference id="reportUserInfoService" interface="com.zh.fee.service.UserInfoService" timeout="20000" retries="0" check="false"/>
2.關(guān)于tomcat的設(shè)置
上文中是springboot開發(fā)環(huán)境,使用了內(nèi)置的tomcat。而在實(shí)際生產(chǎn)環(huán)境中一般用的是外置tomcat來(lái)部署(便于后續(xù)發(fā)布更新),需要在tomcat的配置文件server.xml中設(shè)置超時(shí)時(shí)間(默認(rèn)20秒以下設(shè)置為120秒)。
<Connector port="8811" protocol="HTTP/1.1" connectionTimeout="120000" redirectPort="8443" />
3.關(guān)于Nginx的設(shè)置
如果服務(wù)端使用到Nginx做了反向代理轉(zhuǎn)發(fā)請(qǐng)求,就需要在Nginx的配置文件nginx.conf中設(shè)置超時(shí)時(shí)間,否則會(huì)返回“java.io.IOException: 你的主機(jī)中的軟件中止了一個(gè)已建立的連接”這樣的異常提示。
未設(shè)置時(shí)Nginx響應(yīng)時(shí)間默認(rèn)60秒,這里我將http頭部的keepalive_timeout 、client_header_timeout 、client_body_timeout 、send_timeout 、以及server代碼塊中的proxy_read_timeout 均配置為120秒。
http { include mime.types; default_type application/octet-stream; client_max_body_size 100m; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 120; #連接超時(shí)時(shí)間,服務(wù)器將會(huì)在這個(gè)時(shí)間后關(guān)閉連接 send_timeout 120; #發(fā)送超時(shí)時(shí)間 client_header_timeout 120; #請(qǐng)求頭的超時(shí)時(shí)間 client_body_timeout 120; #請(qǐng)求體的讀超時(shí)時(shí)間 #gzip on; ..... #業(yè)務(wù)系統(tǒng)的配置 server { listen 9092; server_name localhost; location / { proxy_pass http://127.0.0.1:8811/mywebsev/; proxy_read_timeout 120; # 等候后端服務(wù)器響應(yīng)時(shí)間 秒 } } }
保存完記得重啟Nginx。
以上完畢。
總結(jié)
到此這篇關(guān)于springboot如何實(shí)現(xiàn)異步響應(yīng)請(qǐng)求(前端請(qǐng)求超時(shí)的問(wèn)題解決)的文章就介紹到這了,更多相關(guān)springboot實(shí)現(xiàn)異步響應(yīng)請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合mysql、postgres及sqlserver實(shí)現(xiàn)多數(shù)據(jù)源配置實(shí)戰(zhàn)案例
在工作中業(yè)務(wù)的發(fā)展或業(yè)務(wù)數(shù)據(jù)隔離的場(chǎng)景下,通常需要一個(gè)項(xiàng)目中引入多個(gè)數(shù)據(jù)源,但SpringBoot默認(rèn)的自動(dòng)化配置是單數(shù)據(jù)源的,這篇文章主要給大家介紹了關(guān)于SpringBoot整合mysql、postgres及sqlserver實(shí)現(xiàn)多數(shù)據(jù)源配置的相關(guān)資料,需要的朋友可以參考下2023-12-12面試官:java ThreadLocal真的會(huì)造成內(nèi)存泄露嗎
ThreadLocal,java面試過(guò)程中的“釘子戶”,在網(wǎng)上也充斥著各種有關(guān)ThreadLocal內(nèi)存泄露的問(wèn)題,本文換個(gè)角度,先思考ThreadLocal體系中的ThreadLocalMap為什么要設(shè)計(jì)成弱引用2021-08-08Springboot添加jvm監(jiān)控實(shí)現(xiàn)數(shù)據(jù)可視化
這篇文章主要介紹了Springboot添加jvm監(jiān)控實(shí)現(xiàn)數(shù)據(jù)可視化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04java設(shè)計(jì)模式(實(shí)戰(zhàn))-責(zé)任鏈模式
這篇文章主要介紹了java設(shè)計(jì)模式(實(shí)戰(zhàn))-責(zé)任鏈模式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01