springboot自动装配redis在pool下偶尔出现连接异常的问题
jedis pool的配置其实是采用 org.apache.commons.pool2.impl.GenericObjectPoolConfig类的配置项。
jedis 2.9版本代码如下:
package redis.clients.jedis; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; public class JedisPoolConfig extends GenericObjectPoolConfig { public JedisPoolConfig() { // defaults to make your life with connection pool easier :) setTestWhileIdle(true); setMinEvictableIdleTimeMillis(60000); setTimeBetweenEvictionRunsMillis(30000); setNumTestsPerEvictionRun(-1); } }
而springboot的自动装配中对redis连接池的配置:
代码位置:org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool
/** * Pool properties. */ public static class Pool { /** * Max number of "idle" connections in the pool. Use a negative value to indicate * an unlimited number of idle connections. */ private int maxIdle = 8; /** * Target for the minimum number of idle connections to maintain in the pool. This * setting only has an effect if it is positive. */ private int minIdle = 0; /** * Max number of connections that can be allocated by the pool at a given time. * Use a negative value for no limit. */ private int maxActive = 8; /** * Maximum amount of time (in milliseconds) a connection allocation should block * before throwing an exception when the pool is exhausted. Use a negative value * to block indefinitely. */ private int maxWait = -1; public int getMaxIdle() { return this.maxIdle; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public int getMinIdle() { return this.minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxActive() { return this.maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public int getMaxWait() { return this.maxWait; } public void setMaxWait(int maxWait) { this.maxWait = maxWait; } }
问题就出现了:通过spring.redis.pool.xxx的自动装配的配置key其实就比jedis的pool的配置key要少很多,当redis服务端设置了连接空闲的最大时间时,redis服务会kill掉符合条件的空闲的链接,此时客户端的连接池并不会感知连接被kill,当有代码调用pool获取连接时可能会返回一个失效的连接对象,从而导致代码报错。
解决方案:不使用默认的装配。
JedisPoolConfig config = new JedisPoolConfig();
// 最小空闲连接数
config.setMinIdle(props.getMinIdle());
// 最大空闲连接数 config.setMaxIdle(props.getMaxIdle()); // 连接池最大连接数 config.setMaxTotal(props.getMaxActive()); // 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。 config.setMaxWaitMillis(props.getMaxWait()); // 在空闲时检查有效性 config.setTestWhileIdle(props.isTestWhileIdle()); // 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 config.setTestOnBorrow(props.isTestOnBorrow()); // 在return给pool时,是否提前进行validate操作 config.setTestOnReturn(props.isTestOnReturn()); // 表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义 // 该值参考redis server的配置来进行配置 config.setMinEvictableIdleTimeMillis(props.getTimeout()); // 表示idle object evitor两次扫描之间的间隔时间,-1表示不开启这个task config.setTimeBetweenEvictionRunsMillis(props.getTimeout()/2); // 表示idle object evitor每次扫描的最多的对象数
// 建议设置和maxTotal一样大,这样每次可以有效检查所有的链接. config.setNumTestsPerEvictionRun(props.getNumTestsPerEvictionRun());
注意:timeBetweenEvictionRunsMillis和minEvictableIdleTimeMillis的总和应小于 数据库设置的 超时空闲失效时间
如果将 testOnBorrow 和 testOnReturn设置为true将会加重服务的负担,降低服务的性能,最好是通过合理的配置 testWhileIdle、minEvictableIdleTimeMillis、timeBetweenEvictionRunsMillis、numTestsPerEvictionRun来达到达到失效连接的清除工作。