在项目中使用redis做缓存,当运行一段时间后就会出现如下错误:Could not get a resource from the pool,然后在看具体的异常信息就是JedisPool中获取不到jedis对象,也就是说连接池中没有可用的jedis。
自己的第一反应就是把最大链接数(setMaxTotal)调大一些,刚开始设置了100、后来200、在后来2000都不行
然后上网一搜发现大家的回答也都是修改最大连接数,如下demo就是网上一篇博客的解释:
1、产生原因:客户端去redis服务器拿连接(代码描述的是租用对象borrowObject)的时候,池中无可用连接,即池中所有连接被占用,且在等待时候设定的超时时间后还没拿到时,报出此异常。
2、解决办法:调整JedisPoolConfig中maxActive为适合自己系统的阀值。
<bean id="dataJedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">但这个自己也设置了,配置如下:
[color=red]<property name="maxActive" value="5000"/>[/color]
<property name="maxIdle" value="5000"/>
<property name="maxWait" value="10000"/>
<property name="testOnBorrow" value="true"/>
</bean>
#最大活动对象数但是这样设置后,当运行一段时候后还是会报同样的错误,说句不爱听的话最大链接数“1000”这个值真的不小,但还是错误,所以肯定不是这个值的原因。
redis.pool.maxTotal=1000
#最大能够保持idel状态的对象数
redis.pool.maxIdle=100
#最小能够保持idel状态的对象数
redis.pool.minIdle=50
#当池内没有返回对象时,最大等待时间
redis.pool.maxWaitMillis=10000
#当调用borrow Object方法时,是否进行有效性检查
redis.pool.testOnBorrow=true
#当调用return Object方法时,是否进行有效性检查
redis.pool.testOnReturn=true
#“空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.
redis.pool.timeBetweenEvictionRunsMillis=30000
#向调用者输出“链接”对象时,是否检测它的空闲超时;
redis.pool.testWhileIdle=true
# 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.
redis.pool.numTestsPerEvictionRun=50
#表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
MinEvictableIdleTimeMillis=60000
#redis服务器的IP
redis.ip=xxxxxx
#redis服务器的Port
redis1.port=6379
后来在网上找到了一篇文章,文章中的jedis工具类中有三个方法,代码如下:
public class JedisUtils {
private static Log logger = LogFactory.getLog(JedisUtils.class);
/**
* 自动注入Redis连接实例对象线程池
*/
@Autowired
private JedisPool jedisPool;
/**
* 获取Jedis对象
*
* @return
*/
public synchronized Jedis getJedis() {
Jedis jedis = null;
if (jedisPool != null) {
try {
if (jedis == null) {
jedis = jedisPool.getResource();
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
return jedis;
}
/**
* 回收Jedis对象资源
*
* @param jedis
*/
public synchronized void returnResource(Jedis jedis) {
if (jedis != null) {
jedisPool.returnResource(jedis);
}
}
/**
* Jedis对象出异常的时候,回收Jedis对象资源
*
* @param jedis
*/
public synchronized void returnBrokenResource(Jedis jedis) {
if (jedis != null) {
jedisPool.returnBrokenResource(jedis);
}
}
}
有两个回收方法发现没,一个是正常结束时调用释放jedis资源而另一个是在出现异常时调用释放jedis资源。
业务类、方法代码如下:
Jedis jedis = jedisUtils.getJedis();
if(jedis == null){
throw new NullPointerException("Jedis is Null");
}
try{
long queueCurrValue = jedis.llen(messageQueueName);
if(queueCurrValue >= queueMaxSize){
return addFlag;
}
String serizlizetValue = JSON.toJSONString(message);
if(StringUtils.isNotBlank(serizlizetValue)){
long queueLength = jedis.lpush(messageQueueName, serizlizetValue);
if(queueLength - queueCurrValue > 0){
addFlag = true;
}
}
}catch(Exception e){
jedisUtils.returnBrokenResource(jedis);
logger.error(e.getMessage(), e);
}finally{
jedisUtils.returnResource(jedis);
}
看到没在finally{} 代码块和 catch(){} 异常中都调用了相关方法,来释放jedis资源,这样就不会出现之前的那种异常了,当然最大链接数也不用设置那么大,下面看看修改后的配置文件
#最大链接数
MaxTotal=100
#空闲时最大链接数
MaxIdle=20
#空闲最小
MinIdle=8
#链接最大等待时间 (毫秒)
MaxWaitMillis=10000
就这值,再也没出现获取不到资源的的异常,问题解决了,所以问题不是最大链接数小了,而是没有释放资源,所以不管你设置多大的值都会出现异常而且消耗了大量的资源。
单我在项目中使用的时候有如下问题:
// 释放资源
public synchronized void returnResource(Jedis jedis) {
if (jedis != null) {
pool.returnResource(jedis);
}
}
// 出现异常释放资源
public synchronized void returnBrokenResource(Jedis jedis) {
if (jedis != null) {
pool.returnBrokenResource(jedis);
}
}
两个方法 returnResource(jedis)、 和 returnBrokenResource(jedis) 都被画了横线即过时废弃了,而还不知替换方法所以有知道的请指教,先谢谢了!
相关文章推荐:
http://www.codeweblog.com/redis%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA-%E9%85%8D%E7%BD%AE-%E5%8F%8Ajedis%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95/
http://www.codeweblog.com/jedis%E8%BF%9E%E6%8E%A5%E6%B1%A0%E9%85%8D%E7%BD%AE/
http://www.codeweblog.com/jedis-returnresource%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/
http://fengguang0051.iteye.com/blog/2237171
不过其中的两篇文章也会比自己转载做笔记的……