上個月線上生產環境有幾個介面出現例外回應,查看生產日志后發現,如下錯誤

線上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分詞檢索
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/457589.html
標籤:Java
