配色: 字号:
redis常见JedisConnectionException异常分析
2017-03-31 | 阅:  转:  |  分享 
  
http://blog.csdn.net/moxiaomomo/article/details/17588483【redis】常见Jedis
ConnectionException异常分析最近项目开发中用到了Redis,选择了官网推荐的javaclientJedis
。Redis常用命令学习:http://redis.io/commandshttp://redis.io/commandsRedi
s官方推荐Java客户端Jedis(包含了所有Redis命令的实现):https://github.com/xetorthio/j
edishttps://github.com/xetorthio/jedisJedis使用过程中最常见异常JedisConnect
ionException有时确实给我们带来了很多困惑,这个异常通常出现在两个使场景。一、当我们执行如下JedisPool类实例的g
etResource()时抛出can''tgetaresource异常。异常代码如下:redis.clients.jedis.
exceptions.JedisConnectionException:Couldnotgetaresourcefro
mthepoolatredis.clients.util.Pool.getResource(Pool.java:22)分析:
redis.clients.util.Pool.getResource会从JedisPool实例池中返回一个可用的redis连接。
分析源码可知JedisPoolextendsredis.clients.util.Pool?.而Pool
通过commons-pool开源工具包中的org.apache.commons.pool.impl.GenericObjectPo
ol来实现对Jedis实例的管理的。所以我们分析一下GenericObjectPool或许能找到答案。首先看一下common-po
ol的api:http://commons.apache.org/pool/apidocs/index.html?org/apac
he/commons/pool/impl/GenericObjectPool.htmlhttp://commons.apache.
org/pool/apidocs/index.html?org/apache/commons/pool/impl/GenericO
bjectPool.html。其中三个重要个几个属性是:MaxActive:可用连接实例的最大数目,为负值时没有限制。MaxId
le:空闲连接实例的最大数目,为负值时没有限制。Idle的实例在使用前,通常会通过org.apache.commons.pool
.BasePoolableObjectFactory的activateObject()方法使其变得可用。MaxWait:等
待可用连接的最大数目,单位毫秒(millionseconds)。?(注:pool.getResource()方法实际调用的Gen
ericObjectPool类borrowObject()方法,该方法会根据MaxWait变量值在没有可用连接(idle/acti
ve)时阻塞等待知道超时,具体含义参看api。)也就是说当连接池中没有active/idle的连接时,会等待maxWait时间,如
果等待超时还没有可用连接,则抛出Couldnotgetaresourcefromthepool异常。所以为避免这样的
错误,我们应该根据程序实际情况合理设置这三个参数的值,同时在我们获取一个连接的程序方法中也应该合理的处理这个异常,当没有连接可用时
,等待一段时间再获取也许是个比较好的选择。二、当我们获取连接后对redis进行操作时,抛出redis.clients.jedis.
exceptions.JedisConnectionException:java.net.SocketTimeoutExcept
ion:Readtimedout异常。异常代码如下:redis.clients.jedis.exceptions.Jedis
ConnectionException:java.net.SocketTimeoutException:Readtimed
outatredis.clients.jedis.Protocol.process(Protocol.java:79)atre
dis.clients.jedis.Protocol.read(Protocol.java:131)atredis.client
s.jedis.Connection.getIntegerReply(Connection.java:188)atredis.c
lients.jedis.Jedis.sismember(Jedis.java:1266)这是一个比较麻烦的异常,困扰了我一天的时
间。我们都知道Redis是对内存进行操作,速度应该都在毫秒级,这是我们通常的认识,所以当对Redis操作出现几秒的超时时间,你能想
象吗?我们还是先分析一下Jedis的源代码吧,以sadd操作为例:publicLongsadd(finalStringke
y,finalString...members){checkIsInMulti();client.sadd(key,me
mbers);returnclient.getIntegerReply();}client是redis.clients.jedi
s.Client.java的实例,继承关系如下:publicclassClientextendsBinaryClient
implementsCommands;publicclassBinaryClientextendsConnection;
Connection包装了对Redisserver的socket操作,命令写操作通过socket.getOutputStream
()输出流将命令信息发送到redisserver,当写完命令后要通过socket.getInputStream()的到的输入流将
命令执行结果返回,这中间必然会有一个命令执行到结果返回的延时时间,这就是一个Jedis调用redis命令操作所用的时间。需要说明的
是,Redisserver是单线程执行所有连接发送过来的命令的,也就是说不管并发中有多少个client在发送命令,redis-s
erver端是单线程处理的,并按照默认的FIFO方式处理请求,这个可在redis.conf配置文件中配置。关于redisserv
er的详细运行机制参见:http://redis.io/documentation所以client.sadd(key,membe
rs);调用完后只是将命令信息发送到了redisserver端,具体有没有执行要看redisserver的负载情况。然后,通过
client.getIntegerReply();等待(timeout)返回结果。Connection初始化socket时有多种
选择,其中设置sockettimeout的方法如下:publicvoidrollbackTimeout(){try
{?socket.setSoTimeout(timeout);?socket.setKeepAlive(false);}cat
ch(SocketExceptionex){?thrownewJedisException(ex);}}由redis
.clients.jedis.Protocol.DEFAULT_TIMEOUT=2000我们知道默认的超时时间是2秒,这个时
间相对于redis操作内存毫秒级的速度来说已经很长,那我们为什么还会遇到ava.net.SocketTimeoutExceptio
n:Readtimedout异常呢?redis操作内存虽然平均毫秒级的,但当数据量很大时未必都如此快速。在我的开发过程中就遇
到过一个集合到了千万级数据量,一次操作超时时间在秒级是很正常的,而且机器性能很好的情况下已经如此,更何况我们本机开发的机器相对于生
产服务器来说速度会更慢了。所以在初始化JedisPool时应该根据实际情况通过redis.clients.jedis.JedisP
oolConfig合理设置连接池参数,通过edisPool构造方法,合理设置socket读取输入InputStream的超时时间。pool=newJedisPool(config,host,port,100000);注意第四个参数timeout,设置成我们能容忍的超时时间,单位是毫秒。但不知道为什么既然单位是毫秒,为什么参数类型是int而不是long。设置第四个参数后,我在四千万数据量集合上操作最多一次大概超时5秒,问题基本解决。
献花(0)
+1
(本文系关平藏书首藏)