当没有连接可用时,redis为啥会有高产出难题

2020-02-12 02:30 来源:未知

本文讲述了Redis高并发问题的解决办法。分享给大家供大家参考,具体如下:

使用redis也有端时间了,现在讲开发中遇到的几个常见异常总结如下:

转载自:http://blog.csdn.net/fachang/article/details/7984123

异常代码如下:

  • 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)
    分析:
    redis.clients.util.Pool.getResource会从JedisPool实例池中返回一个可用的redis连接。分析源码可知JedisPool extends redis.clients.util.Pool<Jedis> .而Pool<T>是通过
    commons-pool开源工具包中的org.apache.commons.pool.impl.GenericObjectPool来实现对Jedis实例的管理的。所以我们分析一下GenericObjectPool或许能找到答案。
    首先看一下common-pool的api:http://commons.apache.org/pool/apidocs/index.html?org/apache/commons/pool/impl/GenericObjectPool.html。
    其中三个重要个几个属性是:
    MaxActive: 可用连接实例的最大数目,为负值时没有限制。
    MaxIdle: 空闲连接实例的最大数目,为负值时没有限制。Idle的实例在使用前,通常会通过org.apache.commons.pool.BasePoolableObjectFactory<T>的activateObject()方法使其变得可用。
    MaxWait: 等待可用连接的最大数目,单位毫秒(million seconds)。
    (注:pool.getResource()方法实际调用的GenericObjectPool类borrowObject()方法,该方法会根据MaxWait变量值在没有可用连接(idle/active)时阻塞等待知道超时,具体含义参看api。)

也就是说当连接池中没有active/idle的连接时,会等待maxWait时间,如果等待超时还没有可用连接,则抛出Could not get a resource from the pool异常。所以为避免这样的错误,

我们应该根据程序实际情况合理设置这三个参数的值,同时在我们获取一个连接的程序方法中也应该合理的处理这个异常,当没有连接可用时,等待一段时间再获取也许是个比较好的选择。

  • 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操作为例:
  1. public Long sadd(final String key, final String... members) {
  2. checkIsInMulti();
  3. client.sadd(key, members);
  4. return client.getIntegerReply();
  5. }

client是redis.clients.jedis.Client.java的实例,继承关系如下:
public class Client extends BinaryClient implements Commands;
public class BinaryClient extends Connection;
Connection包装了对Redis server的socket操作,命令写操作通过socket.getOutputStream()输出流将命令信息发送到redis server,当写完命令后要通过socket.getInputStream()的到的输入流将
命令执行结果返回,这中间必然会有一个命令执行到结果返回的延时时间,这就是一个Jedis调用redis命令操作所用的时间。
需要说明的是,Redis server是单线程执行所有连接发送过来的命令的,也就是说不管并发中有多少个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 的方法如下:

  1. public void rollbackTimeout() {
  2. try {
  3. socket.setSoTimeout(timeout);
  4. socket.setKeepAlive(false);
  5. } catch (SocketException ex) {
  6. throw new JedisException(ex);
  7. }
  8. }
    由redis.clients.jedis.Protocol.DEFAULT_TIMEOUT = 2000;所以可以考虑把超时时间设长,但需要选个合理的值,否则服务可能会堵塞在这个步骤。

redis为什么会有高并发问题

一、通过JedisPool类实例获取getResource()时抛出can't get a resource异常。

redis的出身决定

异常代码如下:

redis是一种单线程机制的nosql数据库,基于key-value,数据可持久化落盘。由于单线程所以redis本身并没有锁的概念,多个客户端连接并不存在竞争关系,但是利用jedis等客户端对redis进行并发访问时会出现问题。发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

同时,单线程的天性决定,高并发对同一个键的操作会排队处理,如果并发量很大,可能造成后来的请求超时。在远程访问redis的时候,因为网络等原因造成高并发访问延迟返回的问题。

at redis.clients.util.Pool.getResource(Pool.java:22)

解决办法

分析:

1.在客户端将连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。

redis.clients.util.Pool.getResource会从JedisPool实例池中返回一个可用的redis连接。分析源码可知JedisPool extends redis.clients.util.Pool .而Pool是通过

2.服务器角度,利用setnx变向实现锁机制。这个方法在实际环境中如何使用,本人并不清楚。

commons-pool开源工具包中的org.apache.commons.pool2.impl.GenericObjectPool来实现对Jedis实例的管理的。所以我们分析一下GenericObjectPool或许能找到答案。

jedis常见错误分析

其中三个重要个几个属性是:

异常代码1:

MaxActive: 最大连接数。

复制代码 代码如下:redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

MaxIdle: 最大空闲数。

问题分析:redis.clients.util.Pool.getResource会从JedisPool池中返回一个可用的redis连接,关于JedisPool中可用连接的配置有几个重要的参数如下:

MaxWait: 最大等待时间,单位毫秒(million seconds)。

1.MaxActive:可用连接实例的最大数目,为负数的时候没有限制。

当连接池中无可用连接时会会进行等待maxWait时间,若超出泽抛Could not get a resource from the pool异常。

2.MaxIdle:空闲连接实例的最大数目,为负值时没有限制。

所以应根据程序实际情况合理设置这三个参数的值,尽量避免这个异常。

3.MaxWait:等待获取链接的超时时间。

二、对redis进行操作时,抛出redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out异常。

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

异常代码如下:

异常代码2:

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)

遇到这个异常,可能会比较疑惑,redis是对内存的操作,速度一个在毫秒级别,在对redis操作出现秒级别的操作时会让人感觉疑惑,但是本文开头已经说过了,在一些特殊情况下,redis出现超时并不奇怪。jedis在初始化JedisPool时应该根据实际情况通过redis.clients.jedis.JedisPoolConfig合理设置连接池参数,通过redisPool构造方法,设置socket读取输入InputStream的超时时间。

at redis.clients.jedis.Protocol.read(Protocol.java:131)

TAG标签:
版权声明:本文由www.129028.com-澳门金沙唯一官网www129028com发布于编程新闻,转载请注明出处:当没有连接可用时,redis为啥会有高产出难题