最近遇到的鏈接問題我準備從重構的幾個程序(redis和mysql)長鏈接和短鏈接,以及鏈接池和單鏈接等問題用幾篇博客來總結下。java
這個問題的具體發生在java原生程序和redis的交互中。這個問題對我最深入的教訓就是說明獲取鏈接不能迷信鏈接池和原封不動的搬運之前代碼的utils。在鏈接的創建的一開始就應該思考鏈接的穩定性和是否應該關閉鏈接。不然這些問題在線上運行十幾個小時可能纔會暴露,即便知道了問題反過來的排查和修改也會很困難。甚至在重構以前用一種勉強湊合的方式掩蓋問題(給本身挖坑)mysql
java原生程序,內部寫多個線程,定時循環監測(一小時一次),使用了redis鏈接。redis
最開始是採用長鏈接的方式,在main方法中用鏈接池獲取鏈接後分配到各線程,這種方式雖然只建立一次鏈接,但在長時間的程序休眠中,仍然會產生對鏈接的佔用,也會致使掉鏈接的問題,如下是從鏈接池獲取鏈接的代碼(也可拆分紅獲取鏈接池和經過鏈接池單獨獲取鏈接):sql
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後直接退出程序。ui
這種方法產生了很大的資源消耗,也對業務形成影響,主要緣由是盲目使用鏈接池形成的。this
修改後採用傳入配置到線程中,在線程中啓動鏈接,同時修改鏈接池鏈接爲直接獲取鏈接。減小了資源消耗,同時也處理了掉鏈接的問題。並在每次循環結束後關閉redis鏈接 :關閉方法redis.close(); 並在下次循環開始後從新初始化,直接獲取redis對象代碼:.net
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; }