【Redis】Could not get a resource from the pool 實乃叢集配置問題
先說些題外話~自上次確診為鼻竇炎+過敏性鼻炎到現在已經一個月了,最初那會,從下午到晚上頭疼難忍。大概是積勞成疾,以前流鼻涕、打噴嚏的時候從來沒有注意過,結果病根一下爆發。
關鍵在於鎖定問題,開始治療一兩天之後就不會頭疼了。當然,習慣也很重要,再也不敢用力擤鼻子了。
挺過那一陣就好受很多,之後就是鼻塞稍微煩人一些。鼻子的問題很容易串到其他面部器官中去,一旦發展嚴重必然大幅度影響生活質量。
治療方法推薦洗鼻(前兩週先消炎,吃地紅黴素),但注意不要讓醫院賺的太狠,藥包到藥店或者網上買都可以;如果是過敏引發流涕也要吃抗過敏藥。
以上,為同樣深受困擾的小夥伴提供一些經驗。。
【問題】使用jediscluster連線redis叢集,使用jedis執行redis命令,一些時候報Could not get a resource from the pool
【復現】時好時壞,有時失敗後馬上第二次請求相同命令即成功,進一步測試發現竟然與請求的key有關,極難定位問題原因。
【方案】1、考慮為服務端與客戶端版本不匹配問題,而
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
jedis版本已為最新,另有3.X-m1版本,換用之後也無效。此處麻煩大家告知一下“m1”的意思。
換用spring的包裝客戶端,spring-data-redis,亦無效。
從時好時壞這一點來看,版本不是主因。事實上,redis對於版本匹配並不如kafka那般嚴格。
2、考慮為客戶端連線引數問題,結合網上各種經驗調整JedisPoolConfig與JedisCluster構造方法引數,均無效。這裡時間浪費最多,網上資訊龐雜而大部分無用。
3、考慮為服務端單個redis節點配置問題,bind不到外網ip,這點很疑惑。
4、考慮為服務端密碼問題,從slot槽的設計入手,假設在目前的客戶端中,如果請求的key需要轉發到其他節點,則因為沒有驗證密碼而被其他節點拒絕。(實際上這是對部署指令碼或者jedis叢集客戶端不信任)
服務端取消密碼構建集群后問題依舊,但已經接近問題答案。
【解決】定位問題在於叢集節點。
原因排查依據:
(1)命令執行時好時壞,同一命令請求不同key,一些一直成功,其他一直不成功。
(2)查詢所有節點的keys命令從未成功。
(3)debug之下發現不成功的命令請求的節點是內網ip,成功的命令是另一節點的外網ip。
原來,為了測試方便,我使用了外網ip構建叢集。但是,官方提供的redis-trib.rb指令碼帶有極大的迷惑性,在構建時返回螢幕的資訊正確,而自動生成的每個節點的nodes.conf中,表述自身的一行與預期均有出入,如下所示:
圖中139為外網ip,而172則是內網ip。導致客戶端經對key雜湊後得到的槽,所對應的請求節點有可能拿到內網ip,導致無法連線。超時後報錯。
為解決問題,我假設只要該配置存在,節點之間即可相互感知,節點重啟後也不會影響叢集狀態。故而手工將內網ip修改為外網ip,重啟每個redis節點,問題解決。
【目前最佳實踐】
目前認為官方提供用於構建和管理叢集的redis-trib.rb指令碼,對外網ip的支援並不完善。因此,
1、在開發測試階段,通過外網ip構建叢集,通過密碼保證安全性。
2、單個redis配置:bind註釋掉,開啟保護模式,設定密碼和叢集master密碼,ruby client.rb中對應修改密碼。
我的路徑/usr/local/rvm/gems/ruby-2.4.1/gems/redis-4.0.2/lib/redis/client.rb 密碼用就替換,不用就填nil,不是空字串!
3、清除每一個節點的持久化記錄和之前的nodes.conf之後,用外網ip正常構建叢集。
4、手工修改生成的每一個nodes.conf,然後重啟對應的redis節點。
如有理解偏差或更好方案,希望可以告知,大家一起討論。