實例基礎信息:
redis版本:redis-cli 3.2.5
架構:傳統主從架構
背景
一天開發人員通過kibana查看tomcat日志時,發現以下錯誤
RedisMessageListenerContainer 643 - Connection failure occurred. Restarting subscription task after 5000 ms
-問題分析
1)百度查看該報錯,很多帖子解釋原因如下:
client-output-buffer-limit pubsub 32mb 8mb 60 # Redis訂閱客戶端訂閱buffer超過32M或持續60秒超過8M,訂閱立即被關閉!(意思是說客戶端消費速度比較慢,導致產生的消息大量堆積超過了限制客戶端被關閉)
2)根據該解釋查看redis服務器,通過client list查看信息如下,通過截圖信息可以看出這次發現的錯誤于網上的描述并不符合,因為此時client-output-buffer-limit pubsub是空的說明不是這個參數限制導致的
3)為了進一步確定是否是該參數限制導致的錯誤,通過服務器手動設置client-output-buffer-limit參數,將其改為不受限制:
127.0.0.1:6379> config set client-output-buffer-limit "normal 0 0 0 slave 268435456 67108864 60 pubsub 0 0 0"
繼續觀察一段時間發現錯誤仍然以一定的頻率出現,這充分說明了這次錯誤不是client-output-buffer-limit pubsub造成的;
4)通過帖子發現一個有用信息是client-output-buffer-limit pubsub如果達到限制會關閉客戶端,然后客戶端會報此次這個錯誤,那是不是有可能是其他地方將這個客戶端關閉了造成這個錯誤;
5)仔細觀察錯誤發生的頻率,發現這應該是2組發布訂閱產生的錯誤,第一張截圖紅框和綠框標出,每組發生的頻率是3min鐘,分析整個線路server端不會主動關閉client,jedis連接池配置的idle空閑斷開是30min與報錯也不符合,突然想起tomcat連接redis是通過haproxy代理的,是不是haproxy將客戶端殺掉了,查看haproxy配置文件發現:
timeout client:客戶端非活動狀態的超時時長
timeout server:客戶端與服務器端建立連接后,等待服務器端的超時時長
發現這個3分鐘idle將會斷開連接,很有可能是這兩個參數造成的;
6)將haproxy的timeout client、timeout server改成10min reload后等待一段時間發現報錯的頻率成了10min鐘,問題找到了,此次這個錯誤是由于haproxy將idle客戶端殺掉了,造成程序客戶端報錯;
7)與開發人員溝通他們代碼的邏輯是訂閱時發生連接錯誤會繼續嘗試連接,也就是說消息產生端頻率太低超出了haproxy設置端idel時間,haproxy將訂閱客戶端連接關閉了,訂閱端客戶端收到client被關閉后會拋出錯誤并重新建立連接,這就是錯誤產生端原因。
- 暫時解決方案
1)haproxy超時時間仍改回3min,因為后端代理了mysql等服務;
2)暫時繼續沿用目前這種方案,因為程序具備斷開重連機制,雖然redis發布訂閱不能持久化,斷開連接期間發布的消息訂閱端有可能丟失,但是目前業務發布消息很少,幾個月發布一次消息,在發布消息的時候連接斷開的概率還是比較小的,所以暫不處理。 - 永久解決方案
1)采用Rabbit Mq取代redis
2)tomcat直連redis
3)代碼處增加額外的發布信息,確保連接idle時間不會超過3min