最近工作中使用的HttpClient工具遇到的Connection Reset異常。在用戶端和服務端配置不對的時候容易出現問題,下面就是記錄一下如何解決這個問題的過程。
1.用戶端在讀取資料,服務端不再發送新資料(伺服器主動關閉了連接配接)
經過排查線上伺服器配置,發現當一個連接配接空閑時間超過60s,伺服器就會将其關閉。如果剛好用戶端在使用該連接配接則用戶端就會收到來自服務端的連接配接複位标志通知
排查了HttpClient的配置後發現,項目中的HttpClient使用連接配接池,雖然設定了池的最大連接配接數,但是沒有配置空閑連接配接驅逐器(IdleConnectionEvictor)。到這裡原因就已經很明朗了,就是httpClient的配置有問題。
如果說服務端會吧空閑時間超過60s的空閑連接配接關閉掉,導緻了connection reset 異常。要解決這個問題,那隻要用戶端在伺服器關閉連接配接之前把連接配接關閉掉那就不會出現了。是以按着這個思路我對httpClient的配置進行了修改。
為HttpClient添加空閑連接配接驅逐器配置
新加了<code>evictIdleConnections(40, TimeUnit.SECONDS)</code>配置
正常情況下到這裡問題就解決了,但是現實是線上再次出現了Connection Reset異常。繼續排查...
思考:雖然更新配置後再次出現“連接配接重置”異常,不過出現頻率相較于沒改之前還是要低不少。是以改的配置還有用的,肯定是什麼地方沒有配好。為了一探究竟,查了HttpClient關于<code>IdleConnectionEvictor</code>驅逐器的源碼發現了問題所在。
<code>evictIdleConnections(40, TimeUnit.SECONDS)</code>配置的參數在<code>HttpClientBuilder.builder</code>方法中用于執行個體化<code>IdleConnectionEvictor</code>對象的構造參數
調用了<code>connectionEvictor.start()</code>方法啟動了線程驅逐器
sleepTime:延時檢查時間
maxIdleTime:最多空閑時間
結合源碼1和源碼2,可以看到在構造<code>IdleConnectionEvictor</code>時<code>sleepTime</code>和<code>maxIdleTime</code>為同一個值40秒,在這裡還看不出什麼問題,繼續。
通過源碼3我們可以看到,檢查線程的執行周期時間和最大過期時間都是我們傳入的40秒。在這裡停頓一下思考一下,伺服器的空閑連接配接關閉時間是60s,我們配置的時間是40s,那這樣配置會不有出現什麼問題?
線程相隔40s執行一下回收任務,相當于80秒的的周期内會做兩次回收動作。但是60s在其中最多隻能回收掉一次,還是可能存在回收不掉的情況,在不執行回收任務停止的40秒裡面出了connection reset異常了怎麼吧?問題就明了了。
00:00:00 --- 啟動<code>IdleConnectionEvictor.start()</code>,挂起檢查線程,不執行檢查代碼
00:00:10 --- 10秒後的連接配接池建立了一個連接配接
00:00:12 --- 連接配接耗時2s,用完後傳回線程池,假設之後都沒有再被使用了
00:00:40 --- 第一次sleep挂起時間到期,執行檢查任務。發現沒有過期連接配接,下一次回收任務發生在 00:01:20
00:01:12 --- 這時恰好用戶端使用那個空閑的連接配接,服務端關閉了該連接配接。在這裡發生了connection reset 異常
00:01:20 --- 第二次sleep挂起時間到期,執行檢查任務。
服務端空閑連接配接關閉時間是60s,我們用戶端配置的最大空閑時間值應該小于30s才能避免這個問題
在解決方案1的基礎上,把40s時間改為20s,順利解決了該問題。
沐風的原創文章