spring boot自动注入redisTemplate的redis连接池究竟有几个连接,超时时间又是多少
前言redis由于它是一种基于内存操作的高性能分布式数据库,很受大众喜爱,经常出现项目中,可以说是开发必备技能。
·
前言
redis由于它是一种基于内存操作的高性能分布式数据库,很受大众喜爱,经常出现项目中,可以说是开发必备技能。
问题
在开发过程中使用不当会造成redis的各种异常:
- 第一种经常出现读超时:
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
- 第二种获取不到连接
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
springboot默认初始化的连接池和timeout时间
主要看RedisAutoConfiguration类和JedisConnectionFactory类。
如果我们引入starter,基于springboot自动注入原理,类RedisAutoConfiguration会被加载到spring容器中,RedisAutoConfiguration部分代码:
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory()
throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
protected final JedisConnectionFactory applyProperties(
JedisConnectionFactory factory) {
configureConnection(factory);
if (this.properties.isSsl()) {
factory.setUseSsl(true);
}
factory.setDatabase(this.properties.getDatabase());
if (this.properties.getTimeout() > 0) {
factory.setTimeout(this.properties.getTimeout());
}
return factory;
}
省略部分代码
看到会自动注入JedisConnectionFactory这个bean到容器中
public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
private final static Log log = LogFactory.getLog(JedisConnectionFactory.class);
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy(
JedisConverters.exceptionConverter());
private static final Method SET_TIMEOUT_METHOD;
private static final Method GET_TIMEOUT_METHOD;
static {
// We need to configure Jedis socket timeout via reflection since the method-name was changed between releases.
Method setTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "setTimeout", int.class);
if (setTimeoutMethodCandidate == null) {
// Jedis V 2.7.x changed the setTimeout method to setSoTimeout
setTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "setSoTimeout", int.class);
}
SET_TIMEOUT_METHOD = setTimeoutMethodCandidate;
Method getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "getTimeout");
if (getTimeoutMethodCandidate == null) {
getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "getSoTimeout");
}
GET_TIMEOUT_METHOD = getTimeoutMethodCandidate;
}
private JedisShardInfo shardInfo;
private String hostName = "localhost";
private int port = Protocol.DEFAULT_PORT;
private int timeout = Protocol.DEFAULT_TIMEOUT;
private String password;
private boolean usePool = true;
private boolean useSsl = false;
private Pool<Jedis> pool;
private JedisPoolConfig poolConfig = new JedisPoolConfig();
private int dbIndex = 0;
private String clientName;
private boolean convertPipelineAndTxResults = true;
private RedisSentinelConfiguration sentinelConfig;
private RedisClusterConfiguration clusterConfig;
private JedisCluster cluster;
private ClusterCommandExecutor clusterCommandExecutor;
其中的Protocol.DEFAULT_TIMEOUT 默认是2000,单位是毫秒,即读超时时间是默认2秒钟。
再看JedisPoolConfig
public class JedisPoolConfig extends GenericObjectPoolConfig {
public JedisPoolConfig() {
// defaults to make your life with connection pool easier :)
setTestWhileIdle(true);
setMinEvictableIdleTimeMillis(60000);
setTimeBetweenEvictionRunsMillis(30000);
setNumTestsPerEvictionRun(-1);
}
}
public class GenericObjectPoolConfig extends BaseObjectPoolConfig {
/**
* The default value for the {@code maxTotal} configuration attribute.
* @see GenericObjectPool#getMaxTotal()
*/
public static final int DEFAULT_MAX_TOTAL = 8;
/**
* The default value for the {@code maxIdle} configuration attribute.
* @see GenericObjectPool#getMaxIdle()
*/
public static final int DEFAULT_MAX_IDLE = 8;
可以看到默认连接最大是8个,空闲最大也是8个。
异常场景重现
- 读取超时
byte[] n = "abcdef".getBytes();
for (int i = 0; i< 100; i++) {
new Thread(() -> {
jedisOperate.incr(n);
try {
// 模拟耗时操作
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
/**
* @author huangd
* @date 2021-10-08
**/
/**
* @author huangd
* @date 2021-10-08
**/
@Component
public class JedisOperate {
private RedisTemplate redisTemplate;
private Jedis jedis;
JedisOperate(RedisTemplate redisTemplate, CommonProperties properties) {
this.redisTemplate = redisTemplate;
}
@PostConstruct
public void init() {
JedisConnection connection = (JedisConnection) redisTemplate.getConnectionFactory().getConnection();
this.jedis = connection.getNativeConnection();
}
public Long incr(byte[] key) {
Long incr = jedis.incr(key);
return incr;
}
public boolean exists(byte[] key) {
return jedis.exists(key);
}
}
以上会抛出异常:
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
at redis.clients.jedis.Connection.connect(Connection.java:207)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1767)
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:106)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:888)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:432)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:361)
at redis.clients.util.Pool.getResource(Pool.java:49)
- 获取不到连接
代码做一下修改
byte[] n = "abcdef".getBytes();
for (int i = 0; i< 100; i++) {
new Thread(() -> {
// 不用sleep时间
jedisOperate.incr(n);
}).start();
}
/**
* @author huangd
* @date 2021-10-08
**/
@Component
public class JedisOperate {
private RedisTemplate redisTemplate;
JedisOperate(RedisTemplate redisTemplate, CommonProperties properties) {
this.redisTemplate = redisTemplate;
}
@PostConstruct
public void init() {
// JedisConnection connection = (JedisConnection) redisTemplate.getConnectionFactory().getConnection();
// this.jedis = connection.getNativeConnection();
}
public Long incr(byte[] key) {
Jedis jedis = ((JedisConnection) redisTemplate.getConnectionFactory().getConnection()).getNativeConnection();
Long incr = jedis.incr(key);
return incr;
}
}
两次区别在于第一种是共用一个jedis,第二种是每次都去获取。
抛出异常:
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:16)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:194)
报错原因是因为每次都去获取连接,但是用完又没有close掉,这个也是我们容易忽略的地方,要特别注意。如果使用RedisTemplate组件,spring最终会帮我们调用close方法的。
修改最大连接和超时时间
直接在项目的 application.properties文件或application.yml文件增加以下配置:
以application.yml为例
spring:
redis:
host: 127.0.0.1
port: 6379
pool:
max-active: 16
timeout: 5000
最大连接=16,超时时间5秒。
更多推荐
已为社区贡献6条内容
所有评论(0)