Spring中@Scheduled和HttpClient的連環(huán)坑
前言
本文主要給大家介紹了關(guān)于Spring中@Scheduled和HttpClient的坑,分享出來供大家參考學(xué)習(xí),下面話不多說了,來一起看看詳細(xì)的介紹吧。
曾經(jīng)踩過一個大坑:
由于業(yè)務(wù)特殊性,會定時跑很多定時任務(wù),對業(yè)務(wù)數(shù)據(jù)進(jìn)行補(bǔ)償操作等。
在Spring使用過程中,我們可以使用@Scheduled注解可以方便的實(shí)現(xiàn)定時任務(wù)。
有一天早上突然發(fā)現(xiàn),從前一天晚上某一時刻開始,所有的定時任務(wù)全部都卡死不再運(yùn)行了。
@Scheduled默認(rèn)單線程
經(jīng)排查后發(fā)現(xiàn),我們使用@Scheduled注解默認(rèn)的配置的話,所有的任務(wù)都是單線程去跑的。寫了一個測試的task讓它sleep住,就很容易發(fā)現(xiàn),其他所有的task在時間到的時候都沒有觸發(fā)。
如果需要開啟多線程處理,則需要進(jìn)行如下的配置,設(shè)置一下線程數(shù):
@Configuration public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5)); } }
這樣就解決了如果一個task卡住,會引起所有task全部卡住的問題。
但是為什么會有task卡住呢?
HttpClient默認(rèn)參數(shù)配置
原來,有些task會定時請求外部服務(wù)的restful接口,而HttpClient的配置如下:
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); connManager.setMaxTotal(maxConnection); httpClient = HttpClients.custom() .setConnectionManager(connManager) .build();
在最開始使用HttpClient的時候,根本沒有想這么多,基本也都是用用默認(rèn)配置。
追蹤源碼可以發(fā)現(xiàn),在使用上述方式進(jìn)行配置的時候,HttpClient的timeout時間竟然全部都是-1,也就是說如果對方服務(wù)有問題,HttpClient的請求會永不超時,一直等待。源碼如下:
Builder() { super(); this.staleConnectionCheckEnabled = false; this.redirectsEnabled = true; this.maxRedirects = 50; this.relativeRedirectsAllowed = true; this.authenticationEnabled = true; this.connectionRequestTimeout = -1; this.connectTimeout = -1; this.socketTimeout = -1; this.contentCompressionEnabled = true; }
所以我們這時候必須手動指定timeout時間,問題就解決了。例如:
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); connManager.setMaxTotal(maxConnection); RequestConfig defaultRequestConfig = RequestConfig.custom() .setSocketTimeout(3000) .setConnectTimeout(3000) .setConnectionRequestTimeout(3000) .build(); httpClient = HttpClients.custom() .setDefaultRequestConfig(defaultRequestConfig) .setConnectionManager(connManager) .build();
聯(lián)想到另一個問題
其實(shí)HttpClient的使用過程中也遇到過另外一個配置的問題,就是defaultMaxPerRoute這個參數(shù)。
最開始使用的時候也沒有注意過這個參數(shù),只是設(shè)置過連接池的最大連接數(shù)maxTotal。
defaultMaxPerRoute參數(shù)其實(shí)代表了每個路由的最大連接數(shù)。比如你的系統(tǒng)需要訪問另外兩個服務(wù):google.com 和 bing.com。如果你的maxTotal設(shè)置了100,而defaultMaxPerRoute設(shè)置了50,那么你的每一個服務(wù)的最大請求數(shù)最大只能是50。
那么如果defaultMaxPerRoute沒有設(shè)置呢,追蹤源碼:
public PoolingHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, final long timeToLive, final TimeUnit tunit) { super(); this.configData = new ConfigData(); //這里使用的CPool構(gòu)造方法,第二個參數(shù)即為defaultMaxPerRoute,也就是默認(rèn)為2。 this.pool = new CPool(new InternalConnectionFactory( this.configData, connFactory), 2, 20, timeToLive, tunit); this.pool.setValidateAfterInactivity(2000); this.connectionOperator = Args.notNull(httpClientConnectionOperator, "HttpClientConnectionOperator"); this.isShutDown = new AtomicBoolean(false); }
這里發(fā)現(xiàn),原來默認(rèn)值竟然只有2。怪不得當(dāng)時在高并發(fā)情況下總會出現(xiàn)超時,明明maxTotal已經(jīng)設(shè)的很高。
所以如果你的服務(wù)訪問很多不同的外部服務(wù),并且并發(fā)量比較大,一定要好好配置maxTotal和defaultMaxPerRoute兩個參數(shù)。
所以后來再使用任何新的東西,都有好好看下都什么配置,有疑問的一定要先查一下,不要網(wǎng)上copy一段代碼直接就用。當(dāng)時可能沒問題,但是以后沒準(zhǔn)就被坑了。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Java案例之HashMap集合存儲學(xué)生對象并遍歷
這篇文章主要介紹了Java案例之HashMap集合存儲學(xué)生對象并遍歷,創(chuàng)建一個HashMap集合,鍵是學(xué)號(String),值是學(xué)生對象(Student),存儲三個鍵值對元素并遍歷,下文具體操作需要的朋友可以參考一下2022-04-04Java將字符串String轉(zhuǎn)換為整型Int的兩種方式
這篇文章主要介紹了Java如何將字符串String轉(zhuǎn)換為整型Int,在 Java 中要將 String 類型轉(zhuǎn)化為 int 類型時,需要使用 Integer 類中的 parseInt() 方法或者 valueOf() 方法進(jìn)行轉(zhuǎn)換,本文通過實(shí)例代碼給大家詳細(xì)講解,需要的朋友可以參考下2023-04-04JSP頁面?zhèn)鲄⒊霈F(xiàn)中文亂碼的解決方案
這篇文章主要介紹了JSP頁面?zhèn)鲄⒊霈F(xiàn)中文亂碼的解決方案,非常實(shí)用,需要的朋友可以參考下2014-08-08Springboot jar包遠(yuǎn)程調(diào)試詳解
這篇文章主要為大家詳細(xì)介紹了Springboot jar包遠(yuǎn)程調(diào)試,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-09-09新建springboot項(xiàng)目時,entityManagerFactory報錯的解決
這篇文章主要介紹了新建springboot項(xiàng)目時,entityManagerFactory報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01