Redis client之Jedis線上程執行丟擲異常無法恢復的情形和解決方案
環境概述
1. SpringBoot 1.5.9 註解方式返回單例Jedis物件作為client
2.JedisPool連線配置如下:
max-total: 100 # 連線池最大連線數(使用負值表示沒有限制)
max-wait: 10 # 連線池最大阻塞等待時間(使用負值表示沒有限制)
min-idle: 10 # 連線池中的最小空閒連線
max-idle: 10 # 連線池中的最大空閒連線
問題描述
在執行reids操作過程中,一旦丟擲異常,例如:RedisClient - java.net.SocketTimeoutException: Read timed out,接下來所有的redis操作都是失敗的且無法恢復。
問題程式碼
public String get(String key) {
try {
return jedisClient.get(key);
} catch (Exception e) {
logger.error("get:{} have exception:{}", key, e);
}
return null;
}
原因分析
首先看獲得和引用Jedis例項程式碼:
@Bean(name= "jedisClient")
@Autowired
public Jedis singleJedis(@Value("${spring.redis.host}") String host,
@Value("${spring.redis.port}") int port) {
return new JedisPool(jedisPoolConfig, host, port).getResource();
}
@Autowired
private RedisClient redisClient;
很明顯可以看到,整個context都是一個Jedis例項持有這些連線。那麼這個例項持有的當前連線異常斷開時,應該怎麼辦呢?應該close()掉。因為close的時候會判斷連線的可用性,來決定是否採用新的連線,程式碼如下:
public void close() {
if (this.dataSource != null) {
if (this.client.isBroken()) {//關鍵程式碼,判斷是否broken
this.dataSource.returnBrokenResource(this); #執行歸還broken呼叫
} else {
this.dataSource.returnResource(this);#執行歸還呼叫
}
} else {
this.client.close();
}
}
/** 下面程式碼展示,broken的連線歸還流程 */
protected void returnBrokenResourceObject(T resource) {
try {
this.internalPool.invalidateObject(resource);//呼叫無效物件方法
} catch (Exception var3) {
throw new JedisException("Could not return the resource to the pool", var3);
}
}
public void invalidateObject(T obj) throws Exception {
PooledObject<T> p = (PooledObject)this.allObjects.get(new IdentityWrapper(obj));
if (p == null) {
if (!this.isAbandonedConfig()) {
throw new IllegalStateException("Invalidated object not currently part of this pool");
}
} else {
synchronized(p) {
if (p.getState() != PooledObjectState.INVALID) { //這句程式碼關鍵,沒有設定無效狀態事進行destory操作
this.destroy(p);
}
}
this.ensureIdle(1, false);
}
}
//destory操作中對物件進行了刪除和銷燬操作
private void destroy(PooledObject<T> toDestroy) throws Exception {
toDestroy.invalidate();
this.idleObjects.remove(toDestroy);
this.allObjects.remove(new IdentityWrapper(toDestroy.getObject()));
try {
this.factory.destroyObject(toDestroy);
} finally {
this.destroyedCount.incrementAndGet();
this.createCount.decrementAndGet();
}
}
/** 下面的程式碼展示正常連線歸還流程 */
public void returnResource(T resource) {
if (resource != null) {
this.returnResourceObject(resource);
}
}
public void returnResourceObject(T resource) {
if (resource != null) {
try {
this.internalPool.returnObject(resource);//關鍵程式碼,可以發現正常連線歸還的流程最終並沒有刪除銷燬,也就是說其實可以不歸還,繼續使用,更加高效
} catch (Exception var3) {
throw new JedisException("Could not return the resource to the pool", var3);
}
}
}
分析結論
在執行Redis操作異常時,catch到並執行close操作即可,程式碼如下:
public String get(String key) {
try {
return jedisClient.get(key);
} catch (Exception e) {
logger.error("get:{} have exception:{}", key, e);
jedisClient.close(); //新增此行程式碼
}
return null;
}
經過實際驗證,可以解決異常無法自動恢復的問題,需要指出的是jedis始終是那一個例項,只是持有的連線不同了
更新:隨著排查深入,發現在springboot 1.5.9中,是通過新增
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
依賴來引用jedis,進一步檢視jedis版本是2.9.0,而2.9.0版本已經是是較高的穩定版且是使用佔比最大的版本:
實際上,在使用如此廣泛的引用中,應該由jedis庫再發現異常時,直接進行close(),而不是由業務方來完成此事。
相關文章
- C++程式丟擲異常後執行順序C++
- 中介軟體中丟擲異常,Handler中無法捕獲
- migrate:rollback 時 dropForeign 丟擲的異常解決方案
- Shiro身份驗證丟擲AuthenticationException異常,解決方案Exception
- oracle主動丟擲異常Oracle
- Swift 中 throws 異常丟擲Swift
- 啪,還敢丟擲異常
- 硬碟資料丟失原因和解決方案/資料恢復方法硬碟資料恢復
- hibernate open session in view 丟擲異常解決方法SessionView
- java中異常丟擲後程式碼還會繼續執行嗎Java
- java多執行緒:執行緒體往外丟擲異常的處理機制實踐Java執行緒
- 建構函式中丟擲的異常函式
- 高效Java:丟擲適合抽象的異常 - Kyle CarterJava抽象
- MySQL異常恢復之無主鍵情況下innodb資料恢復的方法MySql資料恢復
- Java異常十一:使用throw丟擲異常物件;throw和throws的區別Java物件
- 擷取Spring框架自動丟擲異常Spring框架
- Redis常見問題和解決辦法梳理Redis
- uni-app開發 常見異常和解決辦法APP
- 易優cms404頁面 丟擲HttpException異常HTTPException
- 介面異常狀態統一處理方案在 Firefox 下無效的原因和解決方案Firefox
- WCF服務端丟擲的異常會跑到客戶端服務端客戶端
- scope="session"和scope="request"--丟擲異常非常的bug+垃圾Session
- Redis快取的主要異常及解決方案Redis快取
- 6_Oracle truncate異常恢復之bbed修復Oracle
- 7_Oracle truncate異常恢復之plsql修復OracleSQL
- Oracle Redo丟失恢復方案Oracle
- [譯] Ruby 2.6 Kernel 的system 方法增加是否丟擲異常引數。
- kali無法執行cobaltstrike3.6解決方案
- rman恢復方案和oracle異機恢復Oracle
- XCode除錯時丟擲異常,定位到某一行程式碼XCode除錯行程
- Golang 迴圈異常丟擲不影響整個請求Golang
- OCP課程48:管理II之使用RMAN執行恢復
- 【伺服器資料恢復】異常斷電導致ESXI無法連線儲存的資料恢復案例伺服器資料恢復
- DG同步異常恢復文件
- SQLServer異常故障恢復(二)SQLServer
- 解決Java執行過程中拋簽名異常的問題Java
- Arthas | 定位線上 Dubbo 執行緒池滿異常執行緒
- 建構函式與解構函式是否可以丟擲異常函式