线上redis问题修复:JedisConnectionException: Unexpected end of stream.
经过:
项目上线后经常报
Unexpected end of stream.; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.
白天平均半个小时报一次,晚上频率低些,但是在测试环境和预发环境就没有出现过这种问题。
当时我们项目是从公司的另一个项目拆分出来的,所有配置和另一个环境基本一样
分析:
JedisConnectionException: Unexpected end of stream这个异常是由于redis服务器端设置了5分钟关闭空闲连接,但是连接池还认为该链接有效,继续使用导致的结果。
使用spring boot、spring data redis这些库,默认会开启空闲连接检测,每30秒执行一次,如果PING命令没有返回PONG,或者60秒内该连接还是空闲,就会被清理释放,一次释放一个(默认)所以正常情况下是不会出现这个异常的。
有一种可能是,一下新建了很多连接,一分钟只释放一个,5分钟后空闲连接仍然没有释放完,这时就会报异常,因此需要调整每次释放资源个数(参数:numTestsPerEvictionRun)
由于spring boot通过application.properties方式,只能设置max-active、max-wait、max-idle、min-idle这四个参数哈。如果要设置numTestsPerEvictionRun,需要通过代码。
解决:
1、新增配置
spring.redis.numTestsPerEvictionRun = 5 spring.redis.maxActive = 50 spring.redis.maxIdle = 50 spring.redis.minIdle = 0
2、新增redis配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Getter @Setter @Component @ConfigurationProperties(prefix = "spring.redis" ) public class RedisProperties { private String host; private int port; private String password; private String maxActive; private String maxIdle; private String minIdle; private String timeout; private String numTestsPerEvictionRun; } |
3、新增RedisConnectionFactory
@Configuration @RequiredArgsConstructor @Slf4j public class RedisConfig { @NonNull private RedisProperties redisProperties; /** * 连接池配置信息 * @return */ @Bean public JedisPoolConfig jedisPoolConfig() { JedisPoolConfig poolConfig=new JedisPoolConfig(); //最大连接数 poolConfig.setMaxIdle(Integer.parseInt(redisProperties.getMaxIdle())); //最小空闲连接数 poolConfig.setMinIdle(Integer.parseInt(redisProperties.getMinIdle())); /*poolConfig.setTestOnBorrow(true); poolConfig.setTestOnReturn(true); poolConfig.setTestWhileIdle(true);*/ poolConfig.setNumTestsPerEvictionRun(Integer.parseInt(redisProperties.getNumTestsPerEvictionRun())); //poolConfig.setTimeBetweenEvictionRunsMillis(60000); //当池内没有可用的连接时,最大等待时间 //poolConfig.setMaxWaitMillis(10000); //------其他属性根据需要自行添加------------- return poolConfig; } /** * jedis连接工厂 * @param jedisPoolConfig * @return */ @Bean public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) { //单机版jedis RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); //设置redis服务器的host或者ip地址 redisStandaloneConfiguration.setHostName(redisProperties.getHost()); //设置密码 redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword())); //设置redis的服务的端口号 redisStandaloneConfiguration.setPort(redisProperties.getPort()); //获得默认的连接池构造器(怎么设计的,为什么不抽象出单独类,供用户使用呢) JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder().usePooling(); //指定jedisPoolConifig来修改默认的连接池构造器(真麻烦,滥用设计模式!) jpcb.poolConfig(jedisPoolConfig); //通过构造器来构造jedis客户端配置 JedisClientConfiguration jedisClientConfiguration = jpcb.build(); //单机配置 + 客户端配置 = jedis连接工厂 JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration); return jedisConnectionFactory; } }
后记:
其实中间出现了一个小插曲,就是第一个改完,没有加红色部分代码(usePooling()),虽然报错频率有了很大改善,但是仍然会有报错出现,最后发现是没有开启redis连接池导致,后面加了开启连接池(usePooling())后,经过一天的观察,没有再出现过该错误现象。
记录一下没有开启连接池和开启连接池时stringRedisTemplate的参数(忽略截图中maxTotal等参数,上面说的50是线上的配置,截图是本地的测试结果,本地配置的8)
开启连接池后:
-----------------------------------------------------------
---------------------------------------------
朦胧的夜 留笔~~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)