Redis报错问题,一个曾纠缠了我三个月一度让我怀疑人生的问题:Could not get a resource from the pool

6,691 阅读4分钟
  • 我写这篇日志是为了纪念这个错误的,它是我遇到过的最坑人的错误,也是我自己独立解决,没有抄袭网上答案的一个问题。之所以发个博客纪念它,确实是太坑了。

  • 初次遇到这个错,还是在去年年底的时候,那个时候,服务器老是出现这个错误,然后redis就无法使用了,然后各种连锁反应接踵而至,搞得我慌的一屁。

  • 2020年春节后三月份来公司后,决定要彻底解决一下。于是,在尝试了各种方法都没有效果的情况下,更换了redis 版本,jedis版本,都咩有一点效果,过段时间还是报这个错,而且错误的日志还是一模一样。

  • 要炸了,如果再不解决,我特么就会天天被这个问题烦死,以后可咋办。我不打算就此放弃治疗,我想,一定是有什么问题没有被发现。我看了看代码,发现连接池的配置没有什么问题。存取值的时候也还没有发现特别大的问题。但是它总是在jedis的那个类取值的代码区域报错,说明,存取的时候出了问题的。

  • 于是看了一下代码。

    /** * 获取抓取任务 * @return */ public static JSONObject takeEmsTraceInfoUploadFromQue() {

    	Jedis jedis = getJedis();
    	
    	if(jedis == null){
    		Logger.info("can not get a jedis from pool...");
    		return null;
    	}
    	// 从jedis中获取
    	String jsonStr = "";
    	try {
    
    		List<String> jsonList = jedis.blpop(500, "ems_traceinfo_upload_que");
    		if (jsonList.size() > 0) {
    			jsonStr = jsonList.get(1);
    		}
    		if (StringUtils.isNotBlank(jsonStr)) {
    			return JSONObject.fromObject(jsonStr);
    		}
    
    	} catch (Exception ex) {
    		ex.printStackTrace();
    	} finally {
    		close(jedis);
    	}
    
    	return null;
    }
    

    123456789101112131415161718192021222324252627282930313233

  • 我怀疑是 blpop 这个方法有什么问题。查了下这个方法
    在这里插入图片描述

  • “会阻塞列表直到等待超时或发现可弹出元素为止” 这句话意思不就是 如果没有元素可取,则会一直等待,那么我如果没有元素不停的去取,这样不就占用了redis 的资源了吗?难道是这里的问题?

  • 如果是这样,我换个取值的方式怎么样,用lpop (因为我存元素的时候是用rpush 的所以用lpop,如果你用lpush存元素,一定要用rpop,这样才像队列)
    在这里插入图片描述*

    由此可见,lpop 在取值的时候是没有等待时间的,有的话就去取,没有的话就返回null。这样就避开了等待的时间。

  • 于是我更换成这样

    /** * 获取抓取任务 * @return */ public static JSONObject takeEmsTraceInfoUploadFromQue() {

    	Jedis jedis = getJedis();
    	try {
    
    		String jsonList = jedis.lpop("ems_traceinfo_upload_que");
    		
    		if (StringUtils.isNotBlank(jsonList)) {
    			return JSONObject.fromObject(jsonList);
    		}
    
    	} catch (Exception ex) {
    		ex.printStackTrace();
    	} finally {
    		close(jedis);
    	}
    
    	return null;
    }
    

    1234567891011121314151617181920212223

  • 再次运行项目之后,再也没有出现Could not get a resource from the pool 这个报错了,而且系统正常运行,说明这样改是正确滴!

  • 网上还有说是 JedisPool 配置的原因,但我改了无数次配置,发现还是报错。于是索性保留之前的配置。如下

    /** * 初始化Redis连接池 */ static { try { JedisPoolConfig config = new JedisPoolConfig(); // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true config.setBlockWhenExhausted(true); // 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数) config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy"); // 是否启用pool的jmx管理功能, 默认true config.setJmxEnabled(true); // 最大空闲连接数, 默认8个 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。 config.setMaxIdle(40); config.setMaxTotal(100); config.setMinIdle(10); // 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException; config.setMaxWaitMillis(1000 * 2000); // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的; config.setTestOnBorrow(true);

    		String serverAddress = Play.configuration.getProperty("redis.server.address");
    		int serverPort = Integer.valueOf(Play.configuration.getProperty("redis.server.port"));
    		String serverPassword = Play.configuration.getProperty("redis.server.password");
    
    		jedisPool = new JedisPool(config, serverAddress, serverPort, 3000, serverPassword);
    	} catch (Exception e) {
    		e.printStackTrace();
    	}
    }
    

    123456789101112131415161718192021222324252627282930

  • 以上就是这个问题的解决思路,大家遇到过这种错误吗?在下方留言一起讨论讨论吧!

总结:

感谢大家阅读完本文章,也为大家准备了Java进阶的架构资料,需要的朋友可以关注微信公众号**【Java程序员聚集地】**获取架构资料。