常见JedisConnectionException异常分析

常见JedisConnectionException异常分析-

转自: https://blog.csdn.net/qq7389/article/details/79479776

在使用redis的过程中,偶然遇到JedisConnectionException。所以想记个笔记,发现网上已经有了博客,所以引用一下,记一下笔记。感谢上面这位友人。

一. 当我们执行如下JedisPool 类实例的getResource() 时抛出can’t get a resource 异常。
异常代码如下:

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:22)

分析:
redis.clients.util.Pool.getResource 会从JedisPool实例池中返回一个可用的redis连接。 分析源码可知JedisPool extends redis.clients.util.Pool .而Pool是通过
commons-pool 开源工具中的 org.apache.commons.pool.impl.GenericObjectPool 来实现对Jedis 实例的管理的。所以我们分析一下GenericObjectPool 或许能找到答案。

首选:看一下common-pool 的api:
其中有三个重要的属性是:
MaxActive: 可用连接实例的最大数目,为负值时没有限制。
MaxIdle: 空闲连接实例的最大数目,为负值时没有限制。idle的是在使用前,通常会通过org.apache.commons.pool.BasePoolableObjectFactory 的activateObject() 方法变得可用。
MaxWait: 等待可用连接的最大数目,单位为毫秒(million seconds).

pool.getResource() 方法实际调用的GenericObjectPool 类的borrowObject() 方法,该方法会根据MaxwWait 变量直在没有可用连接(idle/ active) 时阻塞等待直到超时,具体看api。

也就是说当连接池中没有active/idle的连接时,会等待MaxWait时间,如果等待超时还没有可用连接,则抛出Could not get a resource form the pool 异常。所以需要避免这样的错误,
我们应该根据实际业务来设置这三个参数,同时在我们获取一个连接的程序方法中也应该合理的处理这个异常,当没有连接可用时,等待一段时间再获取也许是个比较好的选择。

二. 当我们获取连接后对redis进行操作时,抛出redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out异常。

异常代码如下:

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
at redis.clients.jedis.Protocol.process(Protocol.java:79)
at redis.clients.jedis.Protocol.read(Protocol.java:131)
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:188)
at redis.clients.jedis.Jedis.sismember(Jedis.java:1266)

这是一个比较麻烦的异常,困扰了我一天的时间。我们都知道Redis是对内存进行操作,速度应该都在毫秒级,这是我们通常的认识,所以当对Redis操作出现几秒的超时时间,你能想象吗?
我们还是先分析一下Jedis的源代码吧,以sadd操作为例:

public Long sadd(final String key, final String... members) {
checkIsInMulti();
client.sadd(key, members);
return client.getIntegerReply();
}

client是redis.clients.jedis.Client.java的实例,继承关系如下:

public class Client extends BinaryClient implements Commandspublic class BinaryClient extends Connection

Connection 包装了对redis server的socket操作,命令写操作通过socket.getOutputStream()输出流将命令信息发送到redis server ,当写完命令后要通过socket.getInputStream() 的到输入流将命令执行结果返回,这中间必然会有一个命令执行到结果返回的延迟时间,这就是一个Jedis调用redis命令操作所用的时间。
需要说明的是redis4.0之前一直是单线程,执行所有连接发送过来的命令的,也就是说不管并发中有多少个client在发送命令。redis-server 端是单线程处理的,并且按照默认的FIFO方式处理请求。

这个可以在redis.conf配置文件中配置。关于redis server的详细运行机制在: http://redis.io/documentation
所以client.sadd(key, members); 调用完后只是将命令信息发送到了redis server端,具体有没有执行要看redis server的负载情况。然后,通过client.getIntegerReply();等待(time out)返回结果。
Connection初始化socket时有多种选择,其中设置socket time out 的方法如下:

public void rollbackTimeout() {
      try {
            socket.setSoTimeout(timeout);
            socket.setKeepAlive(false);
      } catch (SocketException ex) {
            throw new JedisException(ex);
          }
      }

redis.clients.jedis.Protocol.DEFAULT_TIMEOUT = 2000 我们知道默认的超时时间是2秒,这个时间相对于redis操作内存毫秒级的速度来说已经很长,那我们为什么还会遇到
java.net.SocketTimeoutException: Read timed out异常呢?redis操作内存虽然平均毫秒级的,但当数据量很大时未必都如此快速。在我的开发过程中就遇到过一个集合到了千万级数据量,一次操作超时时间在秒级是很正常的,而且机器性能很好的性能下已经如此,更如何我们本机开发的机器相对于生产服务器来说速度会更慢了。所以在初始化JedisPool 时应该根据是实际情况通过 redis.clients.jedis.JedisPoolConfig 合理设置连接池参数,通过jedisPool构造方法,合理设置socket 读取输入InputStream 的超时时间。

pool = new JedisPool(config, host, port, 100000);

注意第四个参数time out,设置成我们能容忍的超时时间,单位是毫秒。但不知道为什么既然单位是毫秒,为什么参数类型是int而不是long。

为什么有时会出现timeout,在四千w数据量集合上操作最多一次大约超时5s,问题基本解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值