最近遇到的連接問題我准備從重構的幾個程序(redis和mysql)長連接和短連接,以及連接池和單連接等問題用幾篇博客來總結下。
這個問題的具體發生在java原生程序和redis的交互中。這個問題對我最深刻的教訓就是說明獲取連接不能迷信連接池和原封不動的搬運以前代碼的utils。在連接的建立的一開始就應該思考連接的穩定性和是否應該關閉連接。否則這些問題在線上運行十幾個小時可能才會暴露,即使知道了問題反過來的排查和修改也會很困難。甚至在重構之前用一種勉強湊合的方式掩蓋問題(給自己挖坑)
應用場景
java原生程序,內部寫多個線程,定時循環監測(一小時一次),使用了redis連接。
最開始是采用長連接的方式,在main方法中用連接池獲取連接后分配到各線程,這種方式雖然只創建一次連接,但在長時間的程序休眠中,仍然會產生對連接的占用,也會導致掉連接的問題,以下是從連接池獲取連接的代碼(也可拆分成獲取連接池和通過連接池單獨獲取連接):
public Jedis createJedisCluterInstance(Map<String, Object> props){
String[] hostPortStr = String.valueOf(this.config.getOrDefault(REDIS_CLUSTER_NODE_PORT,props.get(REDIS_CLUSTER_NODE_PORT))).split(":");
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(Integer.valueOf(String.valueOf(this.config.getOrDefault(REDIS_MAX_TOTAL,props.get(REDIS_MAX_TOTAL)))));
config.setMaxIdle(Integer.valueOf(String.valueOf(this.config.getOrDefault(REDIS_MAX_IDLE,props.get(REDIS_MAX_IDLE)))));
config.setMaxWaitMillis(Integer.valueOf(String.valueOf(this.config.getOrDefault(REDIS_MAX_WAIT_MILLIS,props.get(REDIS_MAX_WAIT_MILLIS)))));
JedisPool jedisPool = new JedisPool(config,hostPortStr[0],Integer.valueOf(hostPortStr[1]));
final Jedis[] jedis= {null};
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
.retryIfException()
.withWaitStrategy(WaitStrategies.fixedWait(1000, TimeUnit.MILLISECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(10))
.build();
try {
retryer.call(() -> {
jedis[0] = jedisPool.getResource();
jedis[0].auth("123");
String value = jedis[0].ping();
return StringUtils.isNotBlank(value);
});
} catch (Exception e) {
LOGGER.error("多次獲取Redis連接失敗!");
}
return jedis[0];
}
當時處理掉連接的方式是采用了定時任務按每分鍾一次的頻率監測,對異常trycatch后直接退出程序。
這種方法產生了很大的資源消耗,也對業務造成影響,主要原因是盲目使用連接池造成的。
修改后采用傳入配置到線程中,在線程中啟動連接,同時修改連接池連接為直接獲取連接。減少了資源消耗,同時也處理了掉連接的問題。並在每次循環結束后關閉redis連接 :關閉方法redis.close(); 並在下次循環開始后重新初始化,直接獲取redis對象代碼:
public Jedis getJedis(Map<String, Object> props){
String[] hostPortStr = String.valueOf(this.config.getOrDefault(REDIS_CLUSTER_NODE_PORT, props.get(REDIS_CLUSTER_NODE_PORT))).split(":");
Jedis jedis = new Jedis(hostPortStr[0],Integer.valueOf(hostPortStr[1]),Integer.valueOf(String.valueOf(this.config.getOrDefault(REDIS_MAX_WAIT_MILLIS, props.get(REDIS_MAX_WAIT_MILLIS)))));
jedis.auth("123");
return jedis;
}