生产环境Redis连接,长时间无响应被服务器断开问题
上个月线上生产环境有几个接口出现异常响应,查看生产日志后发现,如下错误
文章图片
【生产环境Redis连接,长时间无响应被服务器断开问题】线上Redis客户端使用的是SpringBoot
默认的Lettuce
客户端,并且没有指定连接池,connection reset by peer
这个错误是当前客户端连接在不知情的情况下被服务端断开后产生,也就是说当前客户端Redis连接已经在服务端断开了,但是客户端并不知道,当请求进来时,Lettuce
继续使用当前Redis连接请求数据时,就会提示connection reset by peer
。
一般情况下服务端断开连接都会发送FIN
包通知客户端,但是当我在用tcpdump
监控服务端tcp传输后,发现Redis服务端tcp连接在无活动一段时间,比如10分钟后会收到来自客户端的RST
包,然而我的客户端也在使用wireshark抓包中,并没有发送给服务端RST
包,这就很奇怪了,猜测这里是可能是服务器对tcp连接的限制导致,对长时间无活动的tcp连接强制断开处理。所以这里线上环境Redis连接偶尔产生connection reset by peer
错误是被我复现出来了。
既然这里知道是Redis连接长时间无活动后被断开导致的bug,那怎么解决?
博主一开始以为重试可以解决,但是发现事情没有想象的简单。上代码
// 查询Redis
public T getCacheObject(final String key) {
try {
ValueOperations operation = redisTemplate.opsForValue();
return operation.get(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return retryGetCacheObject(key, 3);
}
}
// 重试查询Redis
public T retryGetCacheObject(final String key, int retryCount) {
try {
log.info("retryGetCacheObject, key:{}, retryCount:{}", key, retryCount);
if (retryCount <= 0) {
return null;
}
Thread.sleep(200L);
retryCount--;
ValueOperations operation = redisTemplate.opsForValue();
return operation.get(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return retryGetCacheObject(key, retryCount);
}
}
上面代码的意思是第一次查询Redis发生异常后,每隔200毫秒在查3次。当实际运行时,发现这里会提示三次
connection reset by peer
错误,一直没有取到新的Redis连接。到这里这个问题的我的解决思路其实就是怎么在Redis连接发生异常后,怎么创建一条新的连接进行代替。
不多说直接上代码:
// Lettuce连接工厂
@Autowired
private LettuceConnectionFactory lettuceConnectionFactory;
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public T getCacheObject(final String key) {
try {
ValueOperations operation = redisTemplate.opsForValue();
return operation.get(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return retryGetCacheObject(key, 1);
}
}public T retryGetCacheObject(final String key, int retryCount) {
try {
log.info("retryGetCacheObject, key:{}, retryCount:{}", key, retryCount);
if (retryCount <= 0) {
return null;
}
lettuceConnectionFactory.resetConnection();
Thread.sleep(200L);
retryCount--;
ValueOperations operation = redisTemplate.opsForValue();
return operation.get(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return retryGetCacheObject(key, retryCount);
}
}
在用当前Redis连接获取数据发生异常超过
timeout
间隔后,抛出异常,进入重试方法,使用 lettuceConnectionFactory.resetConnection()
方法进行连接重置,创建一条新的连接后,继续获取数据,从而正常响应客户端。lettuceConnectionFactory
对象是对Lettuce
无池化连接的工厂实现,提供了` lettuceConnectionFactory.getConnection();
lettuceConnectionFactory.initConnection();
lettuceConnectionFactory.resetConnection();
`等获取、初始化、重置连接的方法
配合
springboot
配置timeout
将获取数据的超时时间设置为2秒,从而将接口请求耗时也控制在2秒左右redis:
xx: xx
timeout: 2000
到此生产环境这里
SpringBoot
项目下Lettuce
客户端无池化连接偶尔断开的bug算是解决了最后贴一下实战项目地址newbeemall,newbee-mall商城的mybatis plus版本 实现了优惠卷领取, 支付宝沙箱支付,后台添加搜索,RedisSearch分词检索
推荐阅读
- 算法|高德POI数据生产中的计算机视觉技术
- Dofbot机械臂学习笔记|Dofbot机械臂从零部署笔记(5)——开发环境改造(Jetson Nano升级Ubuntu20.04,支持ROS Noetic、Python3)
- 【.NET6+Modbus】Modbus|【.NET6+Modbus】Modbus TCP协议解析、仿真环境以及基于.NET实现基础通信
- Hadoop-Hive组件部署
- Blazor|Blazor 在开发环境保存机密(User Secrets)
- VUE(uni-app)+SSM|VUE(uni-app)+SSM 微信小程序
- uniapp+vue3+vite实现多环境打包
- Istio实践(1)-环境搭建及应用部署
- Spring|Spring Boot 使用 Redis 共享 Session 代码示例
- 离线安装MySQL