填坑:spring-data-redis(Jedis)

问题: Cloud not get resource from pool

图片1:异常

原因

1.使用的组件
pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.3.RELEASE</version><!--$NO-MVN-MAN-VER$-->
</dependency>

2.配置

开启事务配置,使用过程,方法上未使用@Transactional的注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//开启事务配置
<property name="enableTransactionSupport" value="true"></property>
......
<!--redis操作模版,使用该对象可以操作redis -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
<property name="connectionFactory" ref="redisConnectionFactorySentinel" />
<!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! -->
<property name="keySerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<!--开启事务 -->
<property name="enableTransactionSupport" value="true"></property>
</bean>
......

3.分析代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//代码实现
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 普通缓存获取
* @param key 键
* @return
*/
public Object get(String key){
return key==null?null:redisTemplate.opsForValue().get(key);
}
......
//跟踪源码
org.springframework.data.redis.core.RedisTemplate
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = this.getConnectionFactory();
RedisConnection conn = null;
Object var11;
try {
if (this.enableTransactionSupport) {// 开启事务,此值为:true
conn = RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);
} else {
conn = RedisConnectionUtils.getConnection(factory);
}
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}
RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
T result = action.doInRedis(connToExpose);
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}
var11 = this.postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);//调用释放连接的方法
}
return var11;
}
.....
//跟踪释放连接的代码
//事务开启 connHolder 不为null,get方法没有注解@Transactional,connHolder.isTransactionSyncronisationActive()方法返回false
//事务开启 isConnectionTransactional(conn, factory)为true,没有配置 @Transactional(readOnly = true),TransactionSynchronizationManager.isCurrentTransactionReadOnly()为false
//经过层层判断,未进入任何一个if方法块中,也没调用任何回收redis连接的方法
public static void releaseConnection(RedisConnection conn, RedisConnectionFactory factory) {
if (conn != null) {
RedisConnectionUtils.RedisConnectionHolder connHolder = (RedisConnectionUtils.RedisConnectionHolder)TransactionSynchronizationManager.getResource(factory);//事务开启 connHolder 不为null
if (connHolder != null && connHolder.isTransactionSyncronisationActive()) {//条件不够,不执行
if (log.isDebugEnabled()) {
log.debug("Redis Connection will be closed when transaction finished.");
}
} else {
if (isConnectionTransactional(conn, factory) && TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {//条件不够,不执行
unbindConnection(factory);
} else if (!isConnectionTransactional(conn, factory)) {//条件不够,不执行
if (log.isDebugEnabled()) {
log.debug("Closing Redis Connection");
}
conn.close();
}
}
}
}

综上所述,当spring-data-redis 配置事务的时候,方法使用中不加注解@Transactional,会出现redis连接无法回收,资源池pool 资源耗尽,报异常:Cloud not get resource from pool

解决方法:声明两个RedisTemplate实例

两个RedisTemplate实例?

  • 支持事务:commands要么统一执行,要么都被清除,维护数据完整性;
  • 不支持事务,command立即执行,即时返回执行结果并且更高效;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** Sample Configuration **/
@Configuration
public class RedisTxContextConfiguration {
@Bean
public StringRedisTemplate redisTransactionTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
// explicitly enable transaction support
template.setEnableTransactionSupport(true);
return template;
}
@Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
return template;
}
}