Jedis Pool使用原理及源码

1,JedisPool的使用

<!-- 连接池的配置信息 -->
<bean id="jedisConfig" class="redis.clients.jedis.JedisPoolConfig">
  <!-- 说明一个pool可以有多少个Jedis实例 -->
  <property name="maxActive" value="10" />
  <!-- 最大Idle-->
  <property name="maxIdle" value="5" />
  <!-- 最小Idle -->
  <property name="minIdle" value="1" />
  <!-- 获得一个jedis实例的时候是否检查连接可用性(ping()) -->
  <property name="testOnBorrow" value="true" />
  <!-- return 一个jedis实例给pool时,是否检查连接可用性(ping()) -->
  <property name="testOnReturn" value="true" />
  <!-- idle状态监测用异步线程evict进行检查, -->
  <property name="testWhileIdle" value="true" />
  <!-- 一次最多evict的pool里的jedis实例个数 -->
  <property name="numTestsPerEvictionRun" value="10" />
  <!-- test idle 线程的时间间隔 -->
  <property name="timeBetweenEvictionRunsMillis" value="60000" />
  <!--最大等待wait时间-->
  <property name="maxWait" value="3000" />
  <property name="whenExhaustedAction" value="" />
   //WHEN_EXHAUSTED_FAIL = 0; 直接抛出异常throw new NoSuchElementException("Pool exhausted");
   //WHEN_EXHAUSTED_BLOCK = 1;borrowObject()将会阻塞,直到有可用新的或者空闲的object为止,或者如果配置了maxWait,
   //如果请求阻塞超时,将抛出NoSuchElementException.如果maxWait为负数,请求将会无限制的阻
   //塞下去,默认配置。
   //WHEN_EXHAUSTED_GROW = 2;borrowObject()将会继续创建新的对象,并返回,因此,pool维护的对像数将超出maxActive;
   //
</bean>

public String set(String key, String value) {
    Jedis jedis = null;
    boolean success = true;
    try {
        jedis = this.pool.getResource();
        return jedis.set(key, value);
    }catch (JedisException e) {
        success  = false;
        if(jedis != null){
            pool.returnBrokenResource(jedis);
        }
        throw e;
    }finally{
        if(success && jedis != null){
            this.pool.returnResource(jedis);
        }
    }
}

获取Jedis

pool.getResource();

这个可以直接看Pool的getResource方法,

最终还是GenericObjectPool的borrowObject()方法借用对象

@SuppressWarnings("unchecked")
public T getResource() {
    try {
        return (T) internalPool.borrowObject();
    } catch (Exception e) {
        throw new JedisConnectionException(
                "Could not get a resource from the pool", e);
    }
}

用完归还,调用的是GenericObjectPool的returnObject()方法

pool.returnResource(jedis)
//JedisPool.java
public void returnResource(final BinaryJedis resource) {
    returnResourceObject(resource);
}
//Pool.java
public void returnResourceObject(final Object resource) {
   try {
       internalPool.returnObject(resource);
   } catch (Exception e) {
      throw new JedisException("Could not return the resource to the pool", e);
   }
}

出错,调用的是GenericObjectPool的invalidateObject()方法

最后在JedisFactory的destroyObject()中调用jedis.quit()请求Server关闭连接

pool.returnBrokenResource(jedis)
//JedisPool.java
public void returnBrokenResource(final BinaryJedis resource) {
    returnBrokenResourceObject(resource);
}
//Pool.java
protected void returnBrokenResourceObject(final Object resource) {
    try {
        //失效
        internalPool.invalidateObject(resource);
    } catch (Exception e) {
        throw new JedisException(
                "Could not return the resource to the pool", e);
    }
}
//GenericObjectPool
public void invalidateObject(Object obj)
throws Exception
{
try
{
  if (this._factory != null)
    this._factory.destroyObject(obj);
}
finally {
  synchronized (this) {
    this._numActive -= 1;
    allocate();
  }
}
}
//JedisFactory
public void destroyObject(final Object obj) throws Exception {
    if (obj instanceof Jedis) {
        final Jedis jedis = (Jedis) obj;
        if (jedis.isConnected()) {
            try {
                try {
                    jedis.quit();
                } catch (Exception e) {
                }
                jedis.disconnect();
            } catch (Exception e) {

            }
        }
    }
}
JedisPool源代码
package redis.clients.jedis;

import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool.Config;
import redis.clients.util.Pool;

public class JedisPool extends Pool<Jedis> {

    public JedisPool(final Config poolConfig, final String host) {
        this(poolConfig, host, Protocol.DEFAULT_PORT, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE);
    }

    public JedisPool(String host, int port) {
        this(new Config(), host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE);
    }

    public JedisPool(final String host) {
        this(host, Protocol.DEFAULT_PORT);
    }

    public JedisPool(final Config poolConfig, final String host, int port,
            int timeout, final String password) {
        this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE);
    }

    public JedisPool(final Config poolConfig, final String host, final int port) {
        this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE);
    }

    public JedisPool(final Config poolConfig, final String host, final int port, final int timeout) {
        this(poolConfig, host, port, timeout, null, Protocol.DEFAULT_DATABASE);
    }

    public JedisPool(final Config poolConfig, final String host, int port, int timeout, final String password,
                     final int database) {
        super(poolConfig, new JedisFactory(host, port, timeout, password, database));
    }


    public void returnBrokenResource(final BinaryJedis resource) {
        returnBrokenResourceObject(resource);
    }

    public void returnResource(final BinaryJedis resource) {
        returnResourceObject(resource);
    }

    /**
     * PoolableObjectFactory custom impl.
     */
    private static class JedisFactory extends BasePoolableObjectFactory {
        private final String host;
        private final int port;
        private final int timeout;
        private final String password;
        private final int database;

        public JedisFactory(final String host, final int port,
                final int timeout, final String password, final int database) {
            super();
            this.host = host;
            this.port = port;
            this.timeout = timeout;
            this.password = password;
            this.database = database;
        }

        public Object makeObject() throws Exception {
            final Jedis jedis = new Jedis(this.host, this.port, this.timeout);

            jedis.connect();
            if (null != this.password) {
                jedis.auth(this.password);
            }
            if( database != 0 ) {
                jedis.select(database);
            }

            return jedis;
        }

        public void destroyObject(final Object obj) throws Exception {
            if (obj instanceof Jedis) {
                final Jedis jedis = (Jedis) obj;
                if (jedis.isConnected()) {
                    try {
                        try {
                            jedis.quit();
                        } catch (Exception e) {
                        }
                        jedis.disconnect();
                    } catch (Exception e) {

                    }
                }
            }
        }

        public boolean validateObject(final Object obj) {
            if (obj instanceof Jedis) {
                final Jedis jedis = (Jedis) obj;
                try {
                    return jedis.isConnected();/* && jedis.ping().equals("PONG");*/
                } catch (final Exception e) {
                    return false;
                }
            } else {
                return false;
            }
        }
    }
}

其中JedisFactory继承自BasePoolableObjectFactory,只实现了3个方法

makeObject(),连接,new Socket()

destroyObject()--断开连接,

validateObject()--ping

Pool源代码

package redis.clients.util;

import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;

import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisException;

public abstract class Pool<T> {
    private final GenericObjectPool internalPool;

    public Pool(final GenericObjectPool.Config poolConfig,
            PoolableObjectFactory factory) {
        this.internalPool = new GenericObjectPool(factory, poolConfig);
    }

    @SuppressWarnings("unchecked")
    public T getResource() {
        try {
            return (T) internalPool.borrowObject();
        } catch (Exception e) {
            throw new JedisConnectionException(
                    "Could not get a resource from the pool", e);
        }
    }

    public void returnResourceObject(final Object resource) {
        try {
            internalPool.returnObject(resource);
        } catch (Exception e) {
            throw new JedisException(
                    "Could not return the resource to the pool", e);
        }
    }

    public void returnBrokenResource(final T resource) {
        returnBrokenResourceObject(resource);
    }

    public void returnResource(final T resource) {
        returnResourceObject(resource);
    }

    protected void returnBrokenResourceObject(final Object resource) {
        try {
            //失效
            internalPool.invalidateObject(resource);
        } catch (Exception e) {
            throw new JedisException(
                    "Could not return the resource to the pool", e);
        }
    }

    public void destroy() {
        try {
            internalPool.close();
        } catch (Exception e) {
            throw new JedisException("Could not destroy the pool", e);
        }
    }
}
JedisPoolConfig源代码

public class JedisPoolConfig extends Config {
    public JedisPoolConfig() {
        // defaults to make your life with connection pool easier :)
        setTestWhileIdle(true);
        setMinEvictableIdleTimeMillis(60000);
        setTimeBetweenEvictionRunsMillis(30000);
        setNumTestsPerEvictionRun(-1);
    }

    public int getMaxIdle() {
        return maxIdle;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }

    public int getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public int getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
    }

    public long getMaxWait() {
        return maxWait;
    }

    public void setMaxWait(long maxWait) {
        this.maxWait = maxWait;
    }

    public byte getWhenExhaustedAction() {
        return whenExhaustedAction;
    }

    public void setWhenExhaustedAction(byte whenExhaustedAction) {
        this.whenExhaustedAction = whenExhaustedAction;
    }

    public boolean isTestOnBorrow() {
        return testOnBorrow;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public boolean isTestOnReturn() {
        return testOnReturn;
    }

    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public boolean isTestWhileIdle() {
        return testWhileIdle;
    }

    public void setTestWhileIdle(boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
    }

    public long getTimeBetweenEvictionRunsMillis() {
        return timeBetweenEvictionRunsMillis;
    }

    public void setTimeBetweenEvictionRunsMillis(
            long timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    }

    public int getNumTestsPerEvictionRun() {
        return numTestsPerEvictionRun;
    }

    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
        this.numTestsPerEvictionRun = numTestsPerEvictionRun;
    }

    public long getMinEvictableIdleTimeMillis() {
        return minEvictableIdleTimeMillis;
    }

    public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    }

    public long getSoftMinEvictableIdleTimeMillis() {
        return softMinEvictableIdleTimeMillis;
    }

    public void setSoftMinEvictableIdleTimeMillis(
            long softMinEvictableIdleTimeMillis) {
        this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jedis使用总结 前段时间细节的了解了Jedis使用Jedisredis的java版本的客户端实现。 本文做个总结,主要分享如下内容: 【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】 好了,一个一个来。 一、 Pipeline 官方的说明是:starts a pipeline,which is a very efficient way to send lots of command and read all the responses when you finish sending them。简单点说pipeline适用于批处理。当有大量的操作需要一次性执行的时候,可以用管道。 示例: Jedis jedis = new Jedis(String, int); Pipeline p = jedis.pipelined(); p.set(key,value);//每个操作都发送请求给redis-server p.get(key,value); p.sync();//这段代码获取所有的response 这里我进行了20w次连续操作(10w读,10w写),不用pipeline耗时:187242ms,用pipeline耗时:1188ms,可见使用管道后的性能上了一个台阶。看了代码了解到,管道通过一次性写入请求,然后一次性读取响应。也就是说jedis是:request response,request response,...;pipeline则是:request request... response response的方式。这样无需每次请求都等待server端的响应。 二、 跨jvm的id生成器 谈到这个话题,首先要知道redis-server端是单线程来处理client端的请求的。 这样来实现一个id生成器就非常简单了,只要简单的调用jdeis.incr(key);就搞定了。 你或许会问,incr是原子操作吗,能保证不会出现并发问题吗,不是说了吗,server端是单线程处理请求的。 三、 【跨jvm的锁实现【watch】【multi】】 首先说下这个问题的使用场景,有些时候我们业务逻辑是在不同的jvm进程甚至是不同的物理机上的jvm处理的。这样如何来实现不同jvm上的同步问题呢,其实我们可以基于redis来实现一个锁。 具体事务和监听请参考文章:redis学习笔记之事务 暂时找到三种实现方式: 1. 通过jedis.setnx(key,value)实现 import java.util.Random; import org.apache.commons.pool.impl.GenericObjectPool.Config; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; /** * @author Teaey */ public class RedisLock { //加锁标志 public static final String LOCKED = "TRUE"; public static final long ONE_MILLI_NANOS = 1000000L; //默认超时时间(毫秒) public static final long DEFAULT_TIME_OUT = 3000; public static JedisPool pool; public static final Random r = new Random(); //锁的超时时间(秒),过期删除 public static final int EXPIRE = 5 * 60; static { pool = new JedisPool(new Config(), "host", 6379); } private Jedis jedis; private String key; //锁状态标志 private boolean locked = false; public RedisLock(String key) { this.key = key; this.jedis = pool.getResource(); } public boolean lock(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) < timeout) { if (jedis.setnx(key, LOCKED) == 1) { jedis.expire(key, EXPIRE); locked = true; return locked; } // 短暂休眠,nano避免出现活锁 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } public boolean lock() { return lock(DEFAULT_TIME_OUT); } // 无论是否加锁成功,必须调用 public void unlock() { try { if (locked) jedis.del(key); } finally { pool.returnResource(jedis); } } } 2. 通过事务(multi)实现 由于采纳第一张方法,第二种跟第三种实现只贴了关键代码,望谅解。^_^ public boolean lock_2(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) < timeout) { Transaction t = jedis.multi(); // 开启事务,当server端收到multi指令 // 会将该client的命令放入一个队列,然后依次执行,知道收到exec指令 t.getSet(key, LOCKED); t.expire(key, EXPIRE); String ret = (String) t.exec().get(0); if (ret == null || ret.equals("UNLOCK")) { return true; } // 短暂休眠,nano避免出现活锁 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } 3. 通过事务+监听实现 public boolean lock_3(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) < timeout) { jedis.watch(key); // 开启watch之后,如果key的值被修改,则事务失败,exec方法返回null String value = jedis.get(key); if (value == null || value.equals("UNLOCK")) { Transaction t = jedis.multi(); t.setex(key, EXPIRE, LOCKED); if (t.exec() != null) { return true; } } jedis.unwatch(); // 短暂休眠,nano避免出现活锁 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } 最终采用第一种实现,因为加锁只需发送一个请求,效率最高。 四、 【redis分布式】 最后一个话题,jedis的分布式。在jedis源码里发现了两种hash算法(MD5,MURMUR Hash(默认)),也可以自己实现redis.clients.util.Hashing接口扩展。 List<JedisShardInfo> hosts = new ArrayList<JedisShardInfo>(); //server1 JedisShardInfo host1 = new JedisShardInfo("", 6380, 2000); //server2 JedisShardInfo host2 = new JedisShardInfo("", 6381, 2000); hosts.add(host1); hosts.add(host2); ShardedJedis jedis = new ShardedJedis(hosts); jedis.set("key", "");
使用JedisPool来管理Redis连接池,可以按照以下步骤操作: 1. 首先,确保你已经将Jedis库添加到你的项目。你可以在Maven或Gradle的配置文件添加以下依赖: ```xml <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>版本号</version> </dependency> ``` 2. 在代码导入JedisJedisPool相关的类: ```java import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; ``` 3. 创建JedisPoolConfig对象,并设置连接池的相关属性(如最大连接数、最大空闲连接数等): ```java JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(100); // 设置最大连接数 jedisPoolConfig.setMaxIdle(10); // 设置最大空闲连接数 // 可以根据需要设置其他属性,比如最小空闲连接数、连接超时时间等 ``` 4. 创建JedisPool对象,传入Redis服务器的主机名、端口号、连接超时时间和密码(如果有的话): ```java JedisPool jedisPool = new JedisPool(jedisPoolConfig, "localhost", 6379, 10000, "password"); ``` 注意:如果Redis服务器没有设置密码,可以省略密码参数。 5. 从连接池获取Jedis对象,并使用Jedis对象进行Redis操作: ```java try (Jedis jedis = jedisPool.getResource()) { // 执行Redis操作,如存储键值对 jedis.set("key", "value"); String value = jedis.get("key"); System.out.println(value); } catch (Exception e) { // 异常处理 } ``` 6. 当不再需要使用Redis连接时,记得将Jedis对象还回连接池: ```java jedis.close(); ``` 这就是使用JedisPool来管理Redis连接池的基本步骤。通过合理配置连接池的属性,可以提高Redis操作的性能和效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值