Redis作为当前服务架构不可或缺的Cache,其支持丰富多样的数据结构,Redis在使用中其实也有很多坑,本次博主遇到的坑或许说是Java程序员会遇到的多一点,下面就听博主详细道来。

线上服务堵塞

String key = keyOf(appid);

int retryCount = 3;

int socketRetryCount = 3;

Exception ex = null;

while(retryCount > 0 && socketRetryCount > 0) {

try {

return redisDao.getMap(key);

}catch (Exception e) {

}

}

12月2日被告知服务出现异常,查看日志发现其运行到上述代码getMap方法处后日志就没有内容了。

问题分析

"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最大连接为3000

redis配置中session_max_timeout为0,即永不断开连接

一次修改分析

从以上两点分析得出,redis连接被耗尽,于是查找代码得知由于重写spring-data-redis中的hscan方面导致,代码如下:

RedisConnection rc = redisTemplate.getConnectionFactory().getConnection();

if (rc instanceof JedisConnection) {

JedisConnection JedisConnection = (JedisConnection) rc;

return new ConvertingCursor, Map.Entry>(

JedisConnection.hScan(rawValue(key), cursor, scanOptions),

new Converter, Map.Entry>() {

@Override

public Entry convert(final Entry source) {

return new Map.Entry() {

@Override

public String getKey() {

return hashKeySerializer.deserialize(source.getKey());

}

@Override

public String getValue() {

return hashValueSerializer.deserialize(source.getValue());

}

@Override

public String setValue(String value) {

throw new UnsupportedOperationException(

"Values cannot be set when scanning through entries.");

}

};

}

});

} else {

return hashOps.scan(key, scanOptions);

}

上述代码返回ConvertingCursor后未释放连接,导出连接被占满。

二次修改分析

于是修改代码为正常释放连接

try {

...

}finally {

RedisConnectionUtils.releaseConnection(rc, factory);

}

代码经过上线,再次跑程序查看线上日志发现报了大量的Connection time out.

于是博主就思考是不是由于重写代码不对,尝试使用spring-data-redis的原生代码,即直接调用hashOps.scan(key, scanOptions)方法,再次上线。

上线后观察日志:发现这次不是报Connection time out,日志中大量报Unknown reply:错误。

分析如下:

由于代码是在多线程环境下运行,有几百个线程去调用hscan操作,spring-data-redis原生的代码执行完一次hscan操作后就会关闭连接并返回一个迭代器Cursor,但是遍历Cursor时在本次count后会再次根据游标重新使用该连接进行查询,可是连接却已经被关闭,这时会使用新的连接是可以正常迭代的,但是一旦复用到其他线程使用的连接则会导致报错Unknown reply.

三次修改分析

经过思考后得出结论,redis在执行scan操作时一旦连接被释放,那么scan操作将不会进行下去,则报Connection time out.

查阅官方文档得出结论,redis的scan操作需要full iteration,即最优方式是一个连接将以此scan任务执行完全后释放该连接。

redis-scan-doc

修改代码如下:

RedisConnectionFactory factory = redisTemplate.getConnectionFactory();

RedisConnection rc = factory.getConnection();

if (rc instanceof JedisConnection) {

JedisConnection JedisConnection = (JedisConnection) rc;

Cursor> cursorResult = new ConvertingCursor, Map.Entry>(

JedisConnection.hScan(rawValue(key), cursor, scanOptions),

new Converter, Map.Entry>() {

...

});

return new ScanResult>(cursorResult, factory, rc);}

public void releaseConnection() throws IOException{

IOException ex = null;

if(cursor != null) {

try {

cursor.close();

} catch (IOException e) {

ex = e;

}

}

try {

RedisConnectionUtils.releaseConnection(rc, factory);

} catch (Exception e) {

}

if(ex != null) {

throw ex;

}

}

将连接返回给业务代码,并在业务代码执行完毕后将连接释放,问题解决。

总结

连接一旦开启就必须释放,否则造成内存泄漏或服务堵塞不可用

重写代码时需要谨记仔细查阅官方文档给出的方案并实施

多线程下使用redis的scan操作需要使用一个连接遍历完Cursor,而不能复用连接,否则导致报错Unknown reply.

redis集群scan_Redis scan命令的一次坑相关推荐

  1. redis集群搭建及管理命令

    文章目录 1.集群的搭建 1.1 快速搭建集群 1) 首先,我们可以通过执行start命令来创建出6个节点,这6个节点的IP地址都为本机,而端口号则为30001-30006: 2)接着,我们需要使用c ...

  2. redis集群扩容和缩容_redis集群cluster搭建,扩容缩容

    1.    概要说明 1.1.    概述 本文旨在说明平台部署环境搭建的过程和详细步骤说明,为后面系统部署提供清晰的参考依照. 以下搭建部署的环境,为一个小型的分布式集群环境.满足负载均衡和单点故障 ...

  3. redis集群增加节点和删除节点、或者reshard时报 migrating 错误

    目录 redis安装和创建集群看这里 redis安装 redis集群 Redis集群节点增加 添加节点 节点的 redis.conf  配置 端口 6307 启动新的redis实例 2.添加节点到集群 ...

  4. 在虚拟机上安装redis集群,redis使用版本为4.0.5,本机通过命令客户端可以连接访问,外部主机一直访问不了...

    在虚拟机上安装了redis 4 ,启动后本机客户端可以连接访问,但是外部主机一直访问不了,在使用java代码连接redis集群时报:no reachable node in cluster,原因:在r ...

  5. Redis集群命令行部署工具

    使用之前准备工作: 1)配置好与端口无关的公共redis.conf文件,和工具放在同一目录下 2)配置好与端口相关的模板redis-PORT.conf文件,也和工具放在同一目录下(部署时PORT会被替 ...

  6. redis常用命令及安全Redis集群环境搭建

    2019独角兽企业重金招聘Python工程师标准>>> redis 安装 在centos 上很简单 yum install redis 即可完成redis的安装 安装redis cd ...

  7. 5 数据库缓存机制 redis集群 --SoringBoot整合redis--及redis命令集

    Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件 1 上传安装包 2 解压Redis tar -xvf redis-5.0.4.tar.gz 3 安 ...

  8. Redis集群解决方案比较

    调研比较了三个Redis集群的解决方案: 系统 贡献者 是否官方Redis实现 编程语言 Twemproxy Twitter 是 C Redis Cluster Redis官方 是 C Codis 豌 ...

  9. Redis 集群规范

    本文档翻译自 http://redis.io/topics/cluster-spec . 引言 这个文档是正在开发中的 Redis 集群功能的规范(specification)文档, 文档分为两个部分 ...

最新文章

  1. jquery中ajax的dataType属性包括哪几项
  2. C 语言中” 与””的区别
  3. 用c实现跨平台异常捕获机制
  4. Struts2返回JSON数据的具体应用范…
  5. 一定是h的方式不对阅读_大连二手QH69系列H型钢抛丸机厂商_深蓝永盛二手抛丸机...
  6. 不小心关闭了explorer.exe进程,桌面变白解决办法
  7. 服务器后端 项目代码常用目录图
  8. RocketMQ快速入门之手动创建topic
  9. 如何将Word与Endnote再次相互关联?
  10. 【鲲鹏来了】手把手教你创造一个属于自己的鲲鹏开发者环境
  11. linux的yum命令无法使用在哪里下载_Centos Linux下载rpm软件包(基础环境篇)-从零到无 - 飞翔的小胖猪...
  12. PHPCMS2008调用Uchome系统[空间之星]GET语句
  13. 【毕业设计/Matlab系列】基于matlab的线性调频信号的仿真
  14. 51单片机之逻辑运算指令ANL、ORL、XRL等
  15. GIS坐标系:epsg 3857 vs 4326
  16. 接口(API)设计规范
  17. keyshot渲染玻璃打光_keyshot打光技巧,教你如何制作汽车自由式布光效果
  18. 200多程序员报名杨超越编程大赛 直男及肥宅更喜欢杨超越??
  19. ToggleButtonBar的使用
  20. Deepin UOS 20安装(附带镜像文件)

热门文章

  1. 如何获取上传文件的本地路径
  2. Linux 命令之 tcpdump -- 监听网络流量
  3. python如何读取文件内容求和_使用python对文件中的数值进行累加的实例
  4. 软件项目报价明细表_BIM必学软件之REVIT
  5. java开发中实际遇到的问题总结_内行总结:装修中最容易遇到的16个问题及其解决方式!...
  6. linux编写一个简单的端口扫描程序,小弟我在linux下写了个简单的多线程端口扫描程序,运行时出现有关问题,请问一下(2)...
  7. linux 查看日志_干货 | 名企高频考点之谈谈Linux日志查看方式都有哪些
  8. centos 虚拟机glibc升级_分享Centos6.5升级glibc过程
  9. 配置中文_星球大战:战机中队配置需求公布 支持中文
  10. 工业级以太网交换机的应用领域有哪些?