无法从池中获取资源(SocketTimeoutException:)

43

我正在运行多个工作线程(大约10个)以访问来自Redis Q的数据。
对于此,我正在为Jedis客户端使用无限超时。

Jedis jedis = pool.getResource();
jedis.getClient().setTimeoutInfinite();  

我仍然遇到错误"Could not get a resource from the pool"。下面是堆栈跟踪信息。

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:22)
at Workers.Worker1.met1(Worker1.java:124)
at Workers.Worker1.work(Worker1.java:108)
at org.gearman.impl.worker.WorkerConnectionController$3.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)  

Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
at redis.clients.jedis.Connection.connect(Connection.java:124)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:54)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1657)
at redis.clients.jedis.JedisPool$JedisFactory.makeObject(JedisPool.java:63)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1188)
at redis.clients.util.Pool.getResource(Pool.java:20)
... 6 more  

Caused by: java.net.SocketTimeoutException: connect timed out
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at redis.clients.jedis.Connection.connect(Connection.java:119)
... 11 more

你的 Redis 服务器还活着吗?你能否使用 redis-cli 从客户端连接到它? - Didier Spezia
是的,Redis服务器正在运行,并且可以使用redis-cli连接。 - Vignesh
1
我有同样的问题。Redis正在运行。使用JedisPool时出现问题,当我执行returnResource时。使用Jedis jedis = new Jedis("localhost");没有问题。你解决了这个问题吗? - Vladimir Prudnikov
在我的情况下,这是由于非常小的超时时间(以毫秒为单位)导致的。 - Amir Azizkhani
11个回答

27

我注意到如果Redis未运行,则可能会抛出此异常。提醒一下。


10

根据Rick Hanlon的回答,如果使用Spring Boot与Redis,则会抛出此异常。

如果您正在使用Spring Boot,则仅有Redis的依赖关系是不够的;您还需要从redis.io手动下载并安装Redis到您的机器上,然后从Bash终端运行它:

me@my_pc:/path/to/redis/dir$ ./src/redis-server ./redis.conf

在运行服务器之后,您需要在使用Redis的所有应用程序中添加相关行:

application.properties:
...
spring.redis.host: <yourhost> // usually localhost, but can also be on a LAN
spring.redis.port: <yourport> // usually 6379, but settable in redis.conf

application.yml:

...
spring:
  redis:
    host: <yourhost> // usually localhost, but can also be on a LAN
    port: <yourport> // usually 6379, but settable in redis.conf

1
连接到远程 Redis 实例时,是否需要安装 Redis? - zookastos
1
@zookastos 不,不是这样的。 - mvd
谢谢,我后来通过阅读更多的文档弄清楚了。不过那真是个愚蠢的问题 :) - zookastos

7

它是经常发生还是偶尔发生的?如果是偶尔发生,您可能需要检查连接池的大小。

如果您使用的是JedisPoolConfig,默认连接池大小为8。这可能对您的情况来说太小了。

JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(128);
jedisPool = new JedisPool(poolConfig, HOST, PORT, ...);

你能否给我指点一下关于 https://stackoverflow.com/questions/51408969/nested-exception-is-redis-clients-jedis-exceptions-jedisconnectionexception-cou 这个问题? - Jeff Cook

6
可能的原因:
1 - Redis服务器已关闭或Redis应用程序无响应。
2 - 应用程序无法连接到Redis服务器(防火墙等问题)。
3 - 连接Redis服务器超时。
4 - (Redis)池中的所有连接都处于繁忙状态,无法分配新连接。
情况1和2与基础设施有关。
对于情况3,必须增加连接超时时间(“RedisConnectionTimeout”):
pool = new pool(poolConfig, RedisIp, RedisPort, RedisConnectionTimeout);

对于第四种情况,需要增加最大连接计数("RedisMaximumActiveConnectionCount"):

poolConfig.setMaxTotal(RedisMaximumActiveConnectionCount); 

假设以下或类似实现方式;
private Pool<Jedis> pool =  null;   

private final String RedisIp="10.10.10.11";
private final int RedisPort=6379;
private final String RedisConnectionTimeout=2000;
private final String RedisMaximumWaitTime=1000;
private final String RedisMaximumIdleConnectionCount=20;
private final String RedisMaximumActiveConnectionCount=300;
private final String SentinelActive=false;
private final String SentinelHostList="10.10.10.10:26379,10.10.10.10:26380,10.10.10.10:26381";
private final String SentinelMasterName="sentinel-master-name";

private synchronized void initializePool()
{
    if(pool!=null) return;

    poolConfig poolConfig = new poolConfig();
    poolConfig.setMaxTotal(RedisMaximumActiveConnectionCount); 
    poolConfig.setMaxIdle(RedisMaximumIdleConnectionCount);  
    poolConfig.setMaxWaitMillis(RedisMaximumWaitTime); 

    if(SentinelActive)
    {
        String [] sentinelsArray = SentinelHostList.split(",");

        Set<String> sentinels = new HashSet<>();            
        for(String sentinel : sentinelsArray)
        {
            sentinels.add(sentinel);
        }

        String masterName = SentinelMasterName;

        pool = new JedisSentinelPool(masterName, sentinels, poolConfig, RedisConnectionTimeout);            
    }
    else
    {       
        pool = new pool(poolConfig, RedisIp, RedisPort, RedisConnectionTimeout);
    }

}           

protected Jedis getConnection()
{               
    if(pool==null)
        initializePool();

      Jedis jedis = pool.getResource();

      return jedis;     
}   

3
如果你的代码像这样:
JedisPoolConfig jedisPoolConfig = initPoolConfig();    
jedisPool = new JedisPool(jedisPoolConfig, "*.*.*.*", 6379);  

你可以尝试这样做:
JedisPoolConfig jedisPoolConfig = initPoolConfig();    
jedisPool = new JedisPool(jedisPoolConfig, "*.*.*.*", 6379,10*1000); 

这是因为Redis的默认超时时间为2秒,但程序可能在此时间内已经运行完毕。

1

不确定,但可能您没有将Jedis对象返回到池中,导致redis-server连接限制。

每个工作线程在完成工作后应将Jedis实例返回到池中:

Jedis jedis = jedisPool.getResource();
try {
    jedis.getClient().setTimeoutInfinite();
    // your code here...
    ...
} finally {
    jedisPool.returnResource(jedis);
}

是的 @Jarek,我已经返回资源了。 pool.returnResource(jedis); pool.destroy(); - Vignesh
在我的Java应用程序中,我正在使用Jedis2.0.0.jar文件,redis服务器版本为2.4.13。这是否会引起问题? - Vignesh
3
returnResource是受保护的方法,你不能使用它。你需要使用带资源的try语句块 try (Jedis jedis = pool.getResource()) {} - Maciej Dzikowicki

0

我只想在@mrt和@charlie的答案中添加一点。我们需要检查redis服务器的属性。 对于redis客户端3.2.0及以上版本,我们可以使用JedisShardInfo设置ssl属性,如下所示:

JedisShardInfo shardInfo = new JedisShardInfo(redisServer, redisServerPort, redisTimeout, redisSsl);
shardInfo.setPassword(redisPassword);
Jedis jedis = new Jedis(shardInfo);

并且使用 jedisPool:

jedisPool = new JedisPool(poolConfig, redisServer, redisServerPort, redisTimeout, redisPassword, redisSsl);
jedis = jedisPool.getResource();

poolConfig中,您可以指定各种属性,例如setMaxIdlesetMaxWaitMillissetMaxTotal等。

0
这是我的应用程序遇到的问题,实际上我使用了一个单一对象,被多个函数调用,但这些函数并不是线程安全的。
问题在于对单一对象的多次调用导致了调用的损坏。
就像这样:
Class A{
    Object B;
    function1(){
        B.doSomething();
    }
    function2(){
        B.doSomething();
    }
}

由于它们不是线程安全的,所以我遇到了以下错误:

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Socket is closed

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

这是我修复它的方法:

Class A{
    function1(){
        Object B;
        B.doSomething();
    }
    function2(){    
        Object B;
        B.doSomething();
    }
}

希望能有所帮助


0
在AWS Elastic Cache(Redis)上,如果“Redis AUTH default user access”为“Yes”,您需要像这样在JedisPool中设置SSL=true。
boolaen SSL=true;
jedisPool = new JedisPool(poolConfig, redisServer, redisServerPort, redisTimeout, redisPassword, SSL);

什么应该是密码? - Spartan

0

当Jedis无法访问所需的Redis实例(域名/IP、端口或密码)时,会出现此错误。这可能是因为它们错了,或者之前是正确的,但现在不正确了,或者它们是正确的,但防火墙阻止了对它们的访问等原因。

我知道OP确认已经运行redis-cli来确认连接,但是确定是否在与jedis调用来自相同的位置(可能是应用服务器利用jedis连接到某个redis服务器)会很有帮助。

但是那篇原始帖子是从2012年开始的,所以我不指望会有更新,但我留下这些供其他人参考。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接