【Redis】Redis最大連接數maxTotal設置過小的問題

     最近由於不合理的配置了redis中的最大連接數導致了線上服務間歇性不可用的問題,問題無小事,穩定大於一切。

一、結論

      先直接說結論:併發量激增,redis最大連接數過小,導致獲取redis連接超時,超時導致大量請求阻塞,從而導致客戶端因超時主動關閉連接,服務端大量請求阻塞,無法關閉連接,慢慢積累出現close_wait,大量close_wait出現,舊連接不釋放,新連接無法創建,導致沒法對外提供服務。

     方案:1)redis連接池要根據具體的業務量進行設置,太大連接數過多浪費資源,過小無法獲取連接,影響業務。

     2)不管是請求第三方系統還是緩存,數據庫,大數據系統(Hbase)等中間件,要設置符合預期的timeout和降級操作,否則請求積壓會拖死系統。

     3)當服務出現大量close_wait的情況下,大概率都是服務本身的問題,需要排查響應的代碼。

 

二、現象

2.1 服務模塊圖

服務模塊圖

 

 

 

 

 

 

2.2 翻車現場重現

1)由於業務擴展,訪問大概增加3倍,因此選擇擴容了一倍機器。

2)機器擴容之後,報警來了,說是redis proxy的連接水位找過了70%,op讓減小連接數,之前最大連接數maxTotal設置的3000,運維讓減小到300,沒多想,修改了,上線,proxy不在報警,感覺很好。

3)有個別機器開始間歇性報警,端口不可用,應該是服務內部出現了問題。

 

2.3 查看各種監控(藍色問題機器,棕色正常機器)

端口可用性降低:

業務日誌明顯變少:

mem.used 內存使用驟降 

 

thread.num 線程驟增

 

close_wait 大量出現:

業務日誌:

Caused by: redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool

 

三、原因

       結合之前上線的修改以及業務日誌,應該是併發量大的情況下,無法從redis連接池中獲取到連接(maxTotal設置過小),導致報錯。

3.1 爲什麼獲取不到連接會導致服務不可用?

      看一下redis的配置

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <!-- 最大空閒數 -->
    <property name="maxIdle" value="100" />
    <!-- 連接池的最大數據庫連接數 -->
    <property name="maxTotal" value="300" />
    <!-- 最小空連接數 -->
    <property name="minIdle" value="100" />
    <!-- 最大建立連接等待時間 -->
    <property name="maxWaitMillis" value="1000" />
    <!-- 連接超時時是否阻塞,false時報異常,true阻塞直到超時, 默認true -->
    <property name="blockWhenExhausted" value="true" />
    <!-- 返回連接時,檢測連接是否成功 -->
    <property name="testOnBorrow" value="true" />
    <!--定時對線程池中空閒的鏈接進行validateObject校驗 -->
    <property name="testWhileIdle" value="true" />
    <!--在進行returnObject對返回的connection進行validateObject校驗 -->
    <property name="testOnReturn" value="true" />
</bean>

       看到blockWhenExhausted這個參數是true,說明如果獲取連接失敗,那麼會阻塞maxWaitMills那麼長時間再嘗試獲取連接。那麼很清晰了,最大連接數設置的300,但是併發量比它大一到兩倍,自然會有大量的請求阻塞在獲取連接的階段。

3.2 什麼是close_wait?

       感覺學的東西都還給書本了。

active close:主動關閉的一方

passive close:被動關閉的一方 

3.3 爲什麼會出現大量close_wait?

       上游在請求的過程中也是有超時時間的,當上遊主動發起關閉連接的時候,由於請求在服務端非常慢,收到關閉請求之後就會處於close_wait狀態,慢慢的大量的請求積壓使得連接處於close_wait狀態。

3.4 爲什麼大量close_wait會影響服務接收請求?

      因爲大量的連接處於待關閉狀態,沒法釋放資源來應對新的連接。或者說由於端口未釋放,導致tcp連接失敗。

 

四、復現

      通過微服務來將場景進行復現,服務A:ip:8300;服務B:ip:8200,A.a訪問B.b,接口b內部sleep 20s,A.a對B.b的訪問有2000ms的超時降級,我們觀察一下從A.a發起向B.b訪問過程中的網絡狀態。


發起訪問:
localhost: wahaha$ netstat -anlt | grep "8200\|8300\|Local"
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp46      0      0  *.8300                 *.*                    LISTEN
tcp46      0      0  *.8200                 *.*                    LISTEN
Proto/ID  Flags      Local Address          Foreign Address        (state)

2s以內:
localhost: wahaha$ netstat -anlt | grep "8200\|8300\|Local"
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4       0      0  192.168.0.107.8200     192.168.0.107.65332    ESTABLISHED
tcp4       0      0  192.168.0.107.65332    192.168.0.107.8200     ESTABLISHED
tcp6       0      0  ::1.8300                                      ::1.65331                                     ESTABLISHED
tcp6       0      0  ::1.65331                                     ::1.8300                                      ESTABLISHED
tcp46      0      0  *.8300                 *.*                    LISTEN
tcp46      0      0  *.8200                 *.*                    LISTEN
Proto/ID  Flags      Local Address          Foreign Address        (state)

2s ~ 20s:
localhost: wahaha$ netstat -anlt | grep "8200\|8300\|Local"
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4       0      0  192.168.0.107.8200     192.168.0.107.65332    CLOSE_WAIT
tcp4       0      0  192.168.0.107.65332    192.168.0.107.8200     FIN_WAIT_2
tcp46      0      0  *.8300                 *.*                    LISTEN
tcp46      0      0  *.8200                 *.*                    LISTEN
tcp6       0      0  ::1.8300                                      ::1.65331                                     TIME_WAIT
Proto/ID  Flags      Local Address          Foreign Address        (state)

20s以後:
localhost: wahaha$ netstat -anlt | grep "8200\|8300\|Local"
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp46      0      0  *.8300                 *.*                    LISTEN
tcp46      0      0  *.8200                 *.*                    LISTEN
Proto/ID  Flags      Local Address          Foreign Address        (state)

可以看到在2s ~ 20s直接出現CLOSE_WAIT是客戶端超時主動關閉導致的。

 

五、總結

1)合理的設置redis連接池大小,根據業務進行估算。

2)訪問第三方服務和中間件要做降級。

3)大量close_wait出現大概率是被主動關閉了連接,並且有大量超時請求阻塞。

 


Author:憶之獨秀

Email:[email protected]

註明出處:https://lavorange.blog.csdn.net/article/details/106984845

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章