亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

redis scan命令導(dǎo)致redis連接耗盡,線程上鎖的解決

 更新時間:2020年11月24日 09:19:22   作者:loongshawn  
這篇文章主要介紹了redis scan命令導(dǎo)致redis連接耗盡,線程上鎖的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

使用redis scan方法無法獲取connection,導(dǎo)致線程鎖死。

0、關(guān)鍵字

redis

springboot

redistemplate

scan

try-with-resource

1、異?,F(xiàn)象

應(yīng)用部署后,功能正常使用,但約數(shù)小時左右,部分功能接口異常,接口請求無響應(yīng)。

2、異常排查

查看堆棧信息,jstask pid。首先找到j(luò)ava進(jìn)程pid;輸出堆棧信息至log文件,jstask 30 > stask.log,看到與redis相關(guān)的日志,線程狀態(tài)為waiting。

"pool-13-thread-6" prio=10 tid=0x00007f754800e800 nid=0x71b5 waiting on condition [0x00007f758f0ee000]
  java.lang.Thread.State: WAITING (parking)
  at sun.misc.Unsafe.park(Native Method)
  - parking to wait for <0x0000000779b75f40> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
  at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:583)
  at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:442)
  at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
  at redis.clients.util.Pool.getResource(Pool.java:49)
  at redis.clients.jedis.JedisPool.getResource(JedisPool.java:99)
  at org.reborndb.reborn.RoundRobinJedisPool.getResource(RoundRobinJedisPool.java:300)
  at com.le.smartconnect.adapter.spring.RebornConnectionFactory.getConnection(RebornConnectionFactory.java:43)
  at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:128)
  at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:91)
  at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:78)
  at xxx.run(xxx.java:80)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
  at java.util.concurrent.FutureTask.run(FutureTask.java:262)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)

  Locked ownable synchronizers:
- <0x000000074f529b08> (a java.util.concurrent.ThreadPoolExecutor$Worker)

也就是說,redis連接獲取不到,線程一直在等待可用的redis連接。大概率是應(yīng)用中有功能模塊獲取到連接,并沒有釋放。找到一個功能使用了scan,具體如下:

public void releaseCallbackMessage() throws Exception {
  Cursor<Map.Entry<Object, Object>> cursor = RedisCacheUtils.scan(key) 

  if (cursor == null) {
    logger.info("通過scan(H key, ScanOptions options)方法獲取匹配鍵值對記錄為空");
    return;
  }
  while (cursor.hasNext()) {
    // 遍歷緩存
    Map.Entry<Object, Object> entry = cursor.next();
    String key = String.valueOf(entry.getKey());
    }
  }
}

查看scan源碼,發(fā)現(xiàn)其使用過程中,并未主動釋放connection,而get/set操作均會主動釋放connection

public Cursor<Entry<HK, HV>> scan(K key, ScanOptions options) {

  byte[] rawKey = rawKey(key);
  return template.executeWithStickyConnection(
     (RedisCallback<Cursor<Entry<HK, HV>>>) connection -> new ConvertingCursor<>(connection.hScan(rawKey, options),
        new Converter<Entry<byte[], byte[]>, Entry<HK, HV>>() {

         @Override
         public Entry<HK, HV> convert(final Entry<byte[], byte[]> source) {

           return new Entry<HK, HV>() {

            @Override
            public HK getKey() {
              return deserializeHashKey(source.getKey());
            }

            @Override
            public HV getValue() {
              return deserializeHashValue(source.getValue());
            }

            @Override
            public HV setValue(HV value) {
              throw new UnsupportedOperationException("Values cannot be set when scanning through entries.");
            }
           };
         }
        }));
}

get操作源碼finally中有releaseConnection操作。

@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {

  Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
  Assert.notNull(action, "Callback object must not be null");

  RedisConnectionFactory factory = getRequiredConnectionFactory();
  RedisConnection conn = null;
  try {

   if (enableTransactionSupport) {
     // only bind resources in case of potential transaction synchronization
     conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
   } else {
     conn = RedisConnectionUtils.getConnection(factory);
   }

   boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);

   RedisConnection connToUse = preProcessConnection(conn, existingConnection);

   boolean pipelineStatus = connToUse.isPipelined();
   if (pipeline && !pipelineStatus) {
     connToUse.openPipeline();
   }

   RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
   T result = action.doInRedis(connToExpose);

   // close pipeline
   if (pipeline && !pipelineStatus) {
     connToUse.closePipeline();
   }

   // TODO: any other connection processing?
   return postProcessResult(result, connToUse, existingConnection);
  } finally {
   RedisConnectionUtils.releaseConnection(conn, factory);
  }
}

3、解決方式

scan操作后,主動關(guān)閉游標(biāo),使用try(resource) catch(exception)方式編碼。

1、redis scan操作記住需要主動關(guān)閉cursor,即cursor.close;

2、加強(qiáng)規(guī)范編碼;

try (Cursor<Map.Entry<Object, Object>> cursor = RedisCacheUtils.scan(key)) {

  if (cursor == null) {
    logger.info("通過scan(H key, ScanOptions options)方法獲取匹配鍵值對記錄為空");
    return;
  }

  while (cursor.hasNext()) {
    // 遍歷緩存
    Map.Entry<Object, Object> entry = cursor.next();
    String key = String.valueOf(entry.getKey());
  }
} catch (Exception ex) {
  logger.info(ex.toString());
}

關(guān)于 try-with-resources用法需要提一點的就是,resources對象必須是實現(xiàn)了 java.lang.AutoCloseable接口,才會自動關(guān)閉對象。

補(bǔ)充知識:redis連接未釋放,導(dǎo)致redis連接池滿,從而應(yīng)用服務(wù)不可用的問題定位和解決

版本提交測試驗收后,跑了幾天,今天測試突然跑來說平臺不可用。

1. 我先是試圖登錄平臺,發(fā)現(xiàn)首頁可以進(jìn)入,但是登錄不成功。很顯然是后臺的問題。

2. 再看MQ中,發(fā)現(xiàn)消息堆積在隊列中,未被消費(fèi)掉,同時一點一點變化,說明很有可能是哪里有內(nèi)存或連接的泄露或未釋放。

3. 接著登錄阿里云賬號,查看redis監(jiān)控,發(fā)現(xiàn)連接數(shù)已經(jīng)達(dá)到9000多。

4. 查看日志發(fā)現(xiàn)大量的redis連接拒絕錯誤

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:42)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:84)
at com.***(**.java:58)
at com.***(**.java:86)
at com.***(**.java:27)
at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
at org.apache.log4j.Category.callAppenders(Category.java:206)
at org.apache.log4j.Category.forcedLog(Category.java:391)
at org.apache.log4j.Category.log(Category.java:856)
at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:571)
at com.***(**.java:61)
at com.***(**.java:86)
at com.***(**.java:27)
at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
at org.apache.log4j.Category.callAppenders(Category.java:206)
at org.apache.log4j.Category.forcedLog(Category.java:391)
at org.apache.log4j.Category.log(Category.java:856)
at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:571)
at com.***(**.java:61)
at com.***(**.java:86)
at com.***(**.java:27)
at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)

5. 當(dāng)然后臺的tomcat中也報了其他錯誤,比如:

Exception in thread "Thread-18" java.lang.StackOverflowError
    at java.util.Hashtable.get(Hashtable.java:367)
    at java.util.Properties.getProperty(Properties.java:969)
    at java.lang.System.getProperty(System.java:720)
    at sun.security.action.GetPropertyAction.run(GetPropertyAction.java:86)
    at sun.security.action.GetPropertyAction.run(GetPropertyAction.java:52)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.io.PrintWriter.<init>(PrintWriter.java:116)
    at java.io.PrintWriter.<init>(PrintWriter.java:100)
    at org.apache.log4j.DefaultThrowableRenderer.render(DefaultThrowableRenderer.java:58)
    at org.apache.log4j.spi.ThrowableInformation.getThrowableStrRep(ThrowableInformation.java:87)
    at com.aliyun.openservices.log.log4j.LoghubAppender.append(LoghubAppender.java:116)
    at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
    at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
    at org.apache.log4j.Category.callAppenders(Category.java:206)
    at org.apache.log4j.Category.forcedLog(Category.java:391)
    at org.apache.log4j.Category.log(Category.java:856)
    at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:571)
    at com.***(**.java:61)
    at com.***(**.java:86)
    at com.***(**.java:27)
    at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
    at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
    at org.apache.log4j.Category.callAppenders(Category.java:206)
    at org.apache.log4j.Category.forcedLog(Category.java:391)
    at org.apache.log4j.Category.log(Category.java:856)

6. 但是,還是繼續(xù)查看日志,發(fā)現(xiàn)一直有個 [TaskId]PUPHVUNVJSSMKOTQPKHRSPOMUKKOKLPG [Message]null [Result]0 debug日志,因為只有一直不停的發(fā),且連接不關(guān)閉才可能出現(xiàn)這么多連接的情況。一邊對應(yīng)代碼,發(fā)現(xiàn)是頁面上調(diào)用后臺代碼,發(fā)送給設(shè)備長連接的代碼。

try{
  for(...)
  {  
    jedis = RedisManagerPool.getJedis();
    if(!"NULL".equals(value)) {
      break;
    }
  RedisUtils.return(jedis);
  }
} catch() {
  logger.error();
  RedisUtils.returnBroken(jedis);
} 
return value;

且try中沒有finally塊,很顯然如果條件滿足的話就直接break,并return value。但是RedisUtils.return(jedis)這條語句就未執(zhí)行。然后進(jìn)一步懷疑頁面是否是定時去獲取,通過F12,發(fā)現(xiàn)每10s鐘請求一次,頁面上要獲取設(shè)備的上下行速率。所以會累積這么多的請求。最后,修改也比較簡單,添加finally塊,保證RedisUtils.return(jedis)必定會執(zhí)行。

8. 接下來在另一個開發(fā)環(huán)境繼續(xù)復(fù)現(xiàn),我們將redisManagerPool中設(shè)置maxTotal=300,maxIdle=30。而原先是3000、300,這樣有利于快速復(fù)現(xiàn)。

果然,一上午時間就達(dá)到了300的限制。出現(xiàn)了一樣的問題。

9. 綜上,問題定位清楚,且修復(fù)該問題。

a) 對于oss redis之類的第三方網(wǎng)絡(luò)連接,必須要有finally塊執(zhí)行。否則后續(xù)很容易由于不規(guī)范的編碼,出現(xiàn)這種連接未正常釋放的問題。

b) 定位問題,還是需要有日志。如果單從代碼去查,方向會比較多且很容易浪費(fèi)時間。

c) 修改池大小,縮短復(fù)現(xiàn)時間,快速定位修改。

以上這篇redis scan命令導(dǎo)致redis連接耗盡,線程上鎖的解決就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring Cloud Zuul路由網(wǎng)關(guān)服務(wù)過濾實現(xiàn)代碼

    Spring Cloud Zuul路由網(wǎng)關(guān)服務(wù)過濾實現(xiàn)代碼

    這篇文章主要介紹了Spring Cloud Zuul路由網(wǎng)關(guān)服務(wù)過濾實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • Springboot接收文件與發(fā)送文件實例教程

    Springboot接收文件與發(fā)送文件實例教程

    最近工作中遇到個需求,springboot簡單的上傳文檔或者圖片,并且進(jìn)行操作,操作完后進(jìn)行保存指定路徑,下面這篇文章主要給大家介紹了關(guān)于Springboot接收文件與發(fā)送文件的相關(guān)資料,需要的朋友可以參考下
    2023-05-05
  • mybatis中使用大于小于等于的正確方法

    mybatis中使用大于小于等于的正確方法

    在mybatis中sql是寫在xml映射文件中的,如果sql中有一些特殊字符的話,在解析xml文件的時候就會被轉(zhuǎn)義,下面我們就一起來看一下大于小于等于是怎么轉(zhuǎn)義的
    2021-04-04
  • Java將String字符串帶括號轉(zhuǎn)成List的簡單方法

    Java將String字符串帶括號轉(zhuǎn)成List的簡單方法

    Java中我們有時需要對現(xiàn)有的字符串進(jìn)行切割并轉(zhuǎn)化成一個List集合,這篇文章主要給大家介紹了關(guān)于Java將String字符串帶括號轉(zhuǎn)成List的簡單方法,需要的朋友可以參考下
    2023-03-03
  • 實例講解Java并發(fā)編程之閉鎖

    實例講解Java并發(fā)編程之閉鎖

    這篇文章主要介紹了實例講解Java并發(fā)編程之閉鎖,閉鎖相當(dāng)于一扇門,在閉鎖到達(dá)結(jié)束狀態(tài)之前,這扇門一直是關(guān)閉著的,沒有任何線程可以通過,當(dāng)?shù)竭_(dá)結(jié)束狀態(tài)時,這扇門才會打開并容許所有線程通過,需要的朋友可以參考下
    2015-04-04
  • 詳解Spring事務(wù)回滾的兩種方法

    詳解Spring事務(wù)回滾的兩種方法

    Spring事務(wù)回滾的前提是你當(dāng)前使用的數(shù)據(jù)庫必須支持事務(wù),比如MySQL的Innodb是支持的,但Mysaim則是不支持事務(wù)的,本文就給大家介紹兩種Spring事務(wù)回滾的方法,需要的朋友可以參考下
    2023-07-07
  • 如何解決Spring in action @valid驗證不生效的問題

    如何解決Spring in action @valid驗證不生效的問題

    這篇文章主要介紹了如何解決Spring in action @valid驗證不生效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java歸并排序算法代碼實現(xiàn)

    Java歸并排序算法代碼實現(xiàn)

    歸并(Merge)排序法是將兩個(或兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的,下面這篇文章主要給大家介紹了關(guān)于Java歸并排序算法的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • 如何查看Linux上正在運(yùn)行的所有Java程序列表

    如何查看Linux上正在運(yùn)行的所有Java程序列表

    在linux操作時,經(jīng)常要查看運(yùn)行的項目的進(jìn)程和端口,下面這篇文章主要給大家介紹了關(guān)于如何查看Linux上正在運(yùn)行的所有Java程序列表的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • Spring?Boot指標(biāo)監(jiān)控及日志管理示例詳解

    Spring?Boot指標(biāo)監(jiān)控及日志管理示例詳解

    Spring Boot Actuator可以幫助程序員監(jiān)控和管理SpringBoot應(yīng)用,比如健康檢查、內(nèi)存使用情況統(tǒng)計、線程使用情況統(tǒng)計等,這篇文章主要介紹了Spring?Boot指標(biāo)監(jiān)控及日志管理,需要的朋友可以參考下
    2023-11-11

最新評論