生產環境Redis連接,長時間無響應被服務器斷開問題


上個月線上生產環境有幾個接口出現異常響應,查看生產日志后發現,如下錯誤

線上Redis客戶端使用的是SpringBoot默認的Lettuce客戶端,並且沒有指定連接池,connection reset by peer這個錯誤是當前客戶端連接在不知情的情況下被服務端斷開后產生,也就是說當前客戶端Redis連接已經在服務端斷開了,但是客戶端並不知道,當請求進來時,Lettuce繼續使用當前Redis連接請求數據時,就會提示connection reset by peer

一般情況下服務端斷開連接都會發送FIN包通知客戶端,但是當我在用tcpdump監控服務端tcp傳輸后,發現Redis服務端tcp連接在無活動一段時間,比如10分鍾后會收到來自客戶端的RST包,然而我的客戶端也在使用wireshark抓包中,並沒有發送給服務端RST包,這就很奇怪了,猜測這里是可能是服務器對tcp連接的限制導致,對長時間無活動的tcp連接強制斷開處理。所以這里線上環境Redis連接偶爾產生connection reset by peer錯誤是被我復現出來了。

既然這里知道是Redis連接長時間無活動后被斷開導致的bug,那怎么解決?

博主一開始以為重試可以解決,但是發現事情沒有想象的簡單。上代碼

   // 查詢Redis
    public <T> T getCacheObject(final String key) {
        try {
            ValueOperations<String, T> operation = redisTemplate.opsForValue();
            return operation.get(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return retryGetCacheObject(key, 3);
        }
    }
   // 重試查詢Redis
    public <T> T retryGetCacheObject(final String key, int retryCount) {
        try {
            log.info("retryGetCacheObject, key:{}, retryCount:{}", key, retryCount);
            if (retryCount <= 0) {
                return null;
            }
            Thread.sleep(200L);
            retryCount--;
            ValueOperations<String, T> operation = redisTemplate.opsForValue();
            return operation.get(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return retryGetCacheObject(key, retryCount);
        }
    }

上面代碼的意思是第一次查詢Redis發生異常后,每隔200毫秒在查3次。當實際運行時,發現這里會提示三次connection reset by peer錯誤,一直沒有取到新的Redis連接。

到這里這個問題的我的解決思路其實就是怎么在Redis連接發生異常后,怎么創建一條新的連接進行代替。

不多說直接上代碼:

    // Lettuce連接工廠
    @Autowired
    private LettuceConnectionFactory lettuceConnectionFactory;

    /**
     * 獲得緩存的基本對象。
     *
     * @param key 緩存鍵值
     * @return 緩存鍵值對應的數據
     */
    public <T> T getCacheObject(final String key) {
        try {
            ValueOperations<String, T> operation = redisTemplate.opsForValue();
            return operation.get(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return retryGetCacheObject(key, 1);
        }
    }

    public <T> T retryGetCacheObject(final String key, int retryCount) {
        try {
            log.info("retryGetCacheObject, key:{}, retryCount:{}", key, retryCount);
            if (retryCount <= 0) {
                return null;
            }
            lettuceConnectionFactory.resetConnection();
            Thread.sleep(200L);
            retryCount--;
            ValueOperations<String, T> operation = redisTemplate.opsForValue();
            return operation.get(key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return retryGetCacheObject(key, retryCount);
        }
    }

在用當前Redis連接獲取數據發生異常超過timeout間隔后,拋出異常,進入重試方法,使用 lettuceConnectionFactory.resetConnection()方法進行連接重置,創建一條新的連接后,繼續獲取數據,從而正常響應客戶端。lettuceConnectionFactory對象是對Lettuce無池化連接的工廠實現,提供了 lettuceConnectionFactory.getConnection(); lettuceConnectionFactory.initConnection(); lettuceConnectionFactory.resetConnection();等獲取、初始化、重置連接的方法

配合springboot配置timeout將獲取數據的超時時間設置為2秒,從而將接口請求耗時也控制在2秒左右

  redis:
    xx: xx
    timeout: 2000

到此生產環境這里SpringBoot項目下Lettuce客戶端無池化連接偶爾斷開的bug算是解決了

最后貼一下實戰項目地址newbeemall,newbee-mall商城的mybatis plus版本 實現了優惠卷領取, 支付寶沙箱支付,后台添加搜索,RedisSearch分詞檢索


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM