redis命令释放连接_redisscan命令导致redis连接耗尽,线程上
锁的解决
使⽤redis scan⽅法⽆法获取connection,导致线程锁死。
0、关键字
redis
springboot
redistemplate
scan
try-with-resource
1、异常现象
应⽤部署后,功能正常使⽤,但约数⼩时左右,部分功能接⼝异常,接⼝请求⽆响应。
2、异常排查
查看堆栈信息,jstask pid。⾸先找到java进程pid;输出堆栈信息⾄log⽂件,jstask 30 > stask.log,看到与redis相关的⽇志,线程状态为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 urrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at urrent.locks.LockSupport.park(LockSupport.java:186)
at urrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at s.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:583)
at s.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:442)
at s.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at redis.clients.Resource(Pool.java:49)
at redis.clients.Resource(JedisPool.java:99)
Resource(RoundRobinJedisPool.java:300)
at com.le.smartconnect.adapter.Connection(RebornConnectionFactory.java:43)
at org.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:128)
at org.Connection(RedisConnectionUtils.java:91)
at org.Connection(RedisConnectionUtils.java:78)
at xxx.run(xxx.java:80)
at urrent.Executors$RunnableAdapter.call(Executors.java:471)
at urrent.FutureTask.run(FutureTask.java:262)
at urrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at urrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- <0x000000074f529b08> (a urrent.ThreadPoolExecutor$Worker)
也就是说,redis连接获取不到,线程⼀直在等待可⽤的redis连接。⼤概率是应⽤中有功能模块获取到连接,并没有释放。找到⼀个功能使⽤了scan,具体如下:
public void releaCallbackMessage() throws Exception {
Cursor> cursor = RedisCacheUtils.scan(key)
补习学校哪家好
if (cursor == null) {
logger.info("通过scan(H key, ScanOptions options)⽅法获取匹配键值对记录为空");
return;
}
while (cursor.hasNext()) {
// 遍历缓存
儿童服装设计Map.Entry entry = ();
String key = String.Key());
}
}郑博闻
}
查看scan源码,发现其使⽤过程中,并未主动释放connection,⽽get/t操作均会主动释放connection
public Cursor> scan(K key, ScanOptions options) {
byte[] rawKey = rawKey(key);
uteWithStickyConnection(
(RedisCallback>>) connection -> new ConvertingCursor<>(connection.hScan(rawKey, options),
new Converter, Entry>() {
@Override
public Entry convert(final Entry source) {
return new Entry() {
@Override
public HK getKey() {
return Key());
}
@Override
public HV getValue() {
return Value());
}
@Override
public HV tValue(HV value) {
throw new UnsupportedOperationException("Values cannot be t when scanning through entries.");
}
};
}
}));
}
get操作源码finally中有releaConnection操作。
@Nullable
public T execute(RedisCallback action, boolean expoConnection, boolean pipeline) {
Asrt.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
RedisConnectionFactory factory = getRequiredConnectionFactory();
坑爹什么意思RedisConnection conn = null;
try {
英汉对照新闻
if (enableTransactionSupport) {
// only bind resources in ca of potential transaction synchronization
conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
} el {
conn = Connection(factory);
}
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToU = preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToU.isPipelined();
英语 听力if (pipeline && !pipelineStatus) {
connToU.openPipeline();
}
RedisConnection connToExpo = (expoConnection ? connToU : createRedisConnectionProxy(connToU)); T result = action.doInRedis(connToExpo);
// clo pipeline
if (pipeline && !pipelineStatus) {
connToU.cloPipeline();
}
// TODO: any other connection processing?
return postProcessResult(result, connToU, existingConnection);
} finally {
}
}
3、解决⽅式
scan操作后,主动关闭游标,使⽤try(resource) catch(exception)⽅式编码。
1、redis scan操作记住需要主动关闭cursor,即cursor.clo;
2、加强规范编码;
try (Cursor> cursor = RedisCacheUtils.scan(key)) {
if (cursor == null) {
logger.info("通过scan(H key, ScanOptions options)⽅法获取匹配键值对记录为空");
return;
}
while (cursor.hasNext()) {
/
/ 遍历缓存mgmt
Map.Entry entry = ();
String key = String.Key());
}
} catch (Exception ex) {
logger.String());
}
关于 try-with-resources⽤法需要提⼀点的就是,resources对象必须是实现了 java.lang.AutoCloable接⼝,才会⾃动关闭对象。补充知识:redis连接未释放,导致redis连接池满,从⽽应⽤服务不可⽤的问题定位和解决first lady
版本提交测试验收后,跑了⼏天,今天测试突然跑来说平台不可⽤。
1. 我先是试图登录平台,发现⾸页可以进⼊,但是登录不成功。很显然是后台的问题。
2. 再看MQ中,发现消息堆积在队列中,未被消费掉,同时⼀点⼀点变化,说明很有可能是哪⾥有内存或连接的泄露或未释放。
3. 接着登录阿⾥云账号,查看redis监控,发现连接数已经达到9000多。
4. 查看⽇志发现⼤量的redis连接拒绝错误
redis.ptions.JedisConnectionException: Could not get a resource from the pool
北京计算机培训班
at redis.clients.Resource(Pool.java:42)
at redis.clients.Resource(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.(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.(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. 当然后台的tomcat中也报了其他错误,⽐如:
Exception in thread "Thread-18" java.lang.StackOverflowError
at java.(Hashtable.java:367)
at java.Property(Properties.java:969)
at java.Property(System.java:720)
at sun.curity.action.GetPropertyAction.run(GetPropertyAction.java:86)
at sun.curity.action.GetPropertyAction.run(GetPropertyAction.java:52)
at java.curity.AccessController.doPrivileged(Native Method)
at java.io.PrintWriter.(PrintWriter.java:116)
at java.io.PrintWriter.(PrintWriter.java:100)
at org.apache.der(DefaultThrowableRenderer.java:58)