jedis參數不當引發dubbo服務線程池耗盡異常
現象:一個dubbo服務偶發性的出現個別機器甚至整個集群大量報線程池耗盡的問題。一開始對問題的處理比較粗暴,直接增加了10倍的線程數。但是問題依然偶爾出現,重啟服務就可以暫時解決。后來,發現問題出現頻率有點高,不得不花點時間認真分析了。
實際原因:jedis參數設置不當。實際仔細分析問題后發現每次出現異常最開始都是出現了大量的jedis連接池獲取連接異常:
redis.clients.jedis.exceptions.JedisConnection: Could not get a resource from the pool
...省略其他堆棧信息
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect time out
...省略其他堆棧信息
而這些time out實際是redis節點故障或者網絡抖動引起的。然后看看jedis配置,timeout設置為1000,maxRedirect為2,所以一旦出現redis連接問題,將會導致請求阻塞3s左右。而此服務的qps分攤到單機約為200,阻塞導致了dubbo請求的堆積,進而導致雪崩。處理方法:縮短timeout;去除redirect;根據業務請求考慮增加熔斷組件。
另外通過jstack分析,發現一旦出dubbo線程池耗盡的問題,大量的dubbo處理線程都是處於WAITING(parking)狀態,卡頓在獲取redis連接。分析jedis連接管理相關代碼,jedis連接池使用ReentrantReadWriteLock管理,獲取連接涉及鎖資源的爭奪,而redis server節點無法連接將導致連接池需要嘗試創建新的連接,這個過程會拿走寫鎖,導致其他需要通過讀鎖獲取連接的線程進入等待。所以
也需要根據qps設置maxTotal(這個出現問題的jedis居然設置的連接數為10!)。
spark服務使用jedis訪問redis導致redis服務毛刺
我們所使用的redis cluster集群是個比較大集群,具有數十個節點,集群的讀寫壓力都相當大。其中一部分讀寫操作來自其他同事維護的spark服務,使用jedis訪問。
現象:發現redis的響應時間出現有規律的毛刺。
DBA排查發現redis集群的一個節點的負載相對於其他節點比較高,而且出現大量的cluster slots命令。
分析:分析jedis源碼,jedis每次初始化創建連接都是從第一個節點開始嘗試並執行cluster slots命令來獲取整個redis cluster集群的拓撲信息;如果jedis連接池獲取的連接失效,也會執行renewSlotCache,renewClotCache也是會執行cluster slots命令的。
經過推斷及分析,問題出在spark使用jedis的模式上,spark每個批次任務都會創建新的jedis連接,批次處理完成即銷毀掉,所以spark任務執行過程中會反復執行jedis連接池的初始化進而執行cluster slots命令,而從DBA了解到cluster slots命令是比較耗資源的,所以導致了第一個節點負載比較高(jedis連接串固定的,所以第一個節點總是被執行cluster slots命令)。
處理:通過打散jedis連接串上節點的順序來避免總是固定的第一個節點被用來執行cluster slots。此方法實施后,毛刺確實消失了。
疑問:上述方法可以說只是緩解了redis節點負載不均衡的問題,但是由於對spark使用不太了解,所以暫時不知道是否有辦法在多個批次任務間共享jedis連接。