众所周知Redis有3种常用的框架:
Jedis:Redis的Java实现客户端,提供了比较全面的Redis命令的支持(使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis)
Redisson:实现了分布式和可扩展的Java数据结构(基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作)
Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器(基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作)
踩坑场景
查询Redis时报错,重试多次或重启服务才恢复正常,一段时间后又报错。错误信息为:Caused by:io.lettuce.core.RedisCommandTimeoutException: Command timed out after 10 second(s)
排查原因
排查发现原因是springboot版本升级到2.X,即使没有引入Lettuce,但spring-boot-starter-data-redis中默认引入了lettuce-core,其中自适应拓扑刷新与定时拓扑刷新是默认关闭,导致一段时间未操作redis会自动断开连接,再次操作redis会发生连接池超时的异常。
redis服务器配置中有timeout这一属性,意为当客户端闲置多长秒后关闭连接,如果指定为 0 ,表示关闭该功能。查看沙箱redis库的超时时间设置为3600s=1h,线上超时时间也为3600s。
Lettuce有断线重连的机制,但是断开连接之后并不是立即重连,而是根据一个延时重连的策略来延迟执行重连任务,因此在调用方在重试几次后会出现成功的情况。
测试环节未出现连接超时的问题,因为沙箱环境连接的是测试库,DB同事认为是测试和线上redis的版本不同,驱动不同导致沙箱环境未出现连接超时的情况;上线后测试未出现超时的问题,因为测试时间距离服务启动时间较短,没有超过自动断开连接的时间。
解决方案
开启Lettuce客户端的定时刷新配置,保持心跳。 缺点:新增代码,不熟悉,容易出现新的问题。
redis更换为Jedis客户端,Jedis自动会保持物理连接。 缺点:高并发情况下性能没有Lettuce效果好。
由于是线上问题,方案2操作起来只需要修改pom依赖和配置文件,花费时间最少,所以建议选择Jedis
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>