• 线上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配置类

    @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)

     开启连接池后:

  • 相关阅读:
    sleuth使用说明(入门)
    git学习
    rancher中级(二)(rancher中添加证书及操作虚拟主机)
    rancher中级(一)(rancher的存储,网络)
    rancher初级(搭建+基本操作+web应用部署)
    Docker学习笔记
    面试-框架篇
    面试-核心篇
    面试-基础篇
    「译」JUnit 5 系列:扩展模型(Extension Model)
  • 原文地址:https://www.cnblogs.com/liconglong/p/12939568.html
Copyright © 2020-2023  润新知