Redis客户端链接池

  

使用场景

对于一些大对象,或者初始化过程较长的可复用的对象,咱们若是每次都new对象出来,那么意味着会耗费大量的时间。

咱们能够将这些对象缓存起来,当接口调用完毕后,不是销毁对象,当下次使用的时候,直接从对象池中拿出来便可。

下面以redis客户端举例,说明下链接池的基础实现。
commons-pool2,是经常使用的对象池工具包,实现了对象池中对象的整个生命周期的管理,同时还能够手动指定对象生命周期的调度阀值。
Jedis是java的redis客户端的实现,可以实现对redis单机以及切片集群的连接。使用起来还很方便。
下面使用Jedis和commons-pool实现客户端链接池的管理。java

首先定义生成Jedis连接的工厂git

 1 public class JedisPooledFactory extends BasePooledObjectFactory<Jedis> {
 2 
 3     //jedis server url
 4     private String url  = null;
 5 
 6     //redis server port
 7     private int    port = 6379;
 8 
 9     /**
10      * @param url
11      * @param port
12      */
13     public JedisPooledFactory(String url, int port) {
14         super();
15         this.url = url;
16         this.port = port;
17     }
18 
19     /** 
20      * @see org.apache.commons.pool2.BasePooledObjectFactory#create()
21      */
22     @Override
23     public Jedis create() throws Exception {
24         Assert.notNull(url);
25         return new Jedis(url, port);
26     }
27 
28     @Override
29     public boolean validateObject(PooledObject<Jedis> p) {
30         //if closed,validate error
31         if(!p.getObject().isConnected()){
32             return false;
33         }
34         return super.validateObject(p);
35     }
36 
37     @Override
38     public void destroyObject(PooledObject<Jedis> p) throws Exception {
39         // close the connection
40         p.getObject().close();
41         super.destroyObject(p);
42     }
43 
44     /**
45      * @see org.apache.commons.pool2.BasePooledObjectFactory#wrap(java.lang.Object)
46      */
47     @Override
48     public PooledObject<Jedis> wrap(Jedis obj) {
49         return new DefaultPooledObject<Jedis>(obj);
50     }
51 }
jedis链接工厂

咱们能够看到,这个工厂主要是实现了对Jedis链接对象的生命周期的管理,结合Jedis来讲明定义的行为:1.怎么建立Jedis链接(好比链接池中jedis链接不够用的时候)。2.怎么销毁对象(好比链接池中大量空闲链接)。3.每次borrow/return Jedis链接的时候,判断jedis链接的有效性。,若是无效就将该对象销毁,而后从新borrow。4.wrap,将任意对象池化的时候,须要让对象支持一些对象池中的特定的一些特性,好比在对象池中,若是空闲对象超过了阀值而且超过了必定的时间,borrow的时候就清除掉对象,这个意味着池中的对象须要支持池化后的一些特性,主要是与生命状态相关的特性。那么这个wrap就是对象的包装类,有个默认的实现:redis

DefaultPooledObject

咱们如今要开始使用Jedis链接工厂了apache

  1 public class RedisClientImpl implements InitializingBean, RedisClient {
  2 
  3     private final static Logger      logger      = LoggerFactory.getLogger(RedisClientImpl.class);
  4 
  5     /** redis url */
  6     private String                   url         = null;
  7     private int                      port        = 6379;
  8 
  9     /**
 10      * The Max wait time.
 11      */
 12     private int                      maxWaitTime = 1000;
 13 
 14     /** jedis池化 */
 15     private GenericObjectPool<Jedis> jedisPool   = null;
 16 
 17     /**
 18      * Instantiates a new Redis client.
 19      */
 20     public RedisClientImpl() {
 21     }
 22 
 23     /**
 24      * Instantiates a new Redis client.
 25      *
 26      * @param url  the url
 27      * @param port the port
 28      */
 29     public RedisClientImpl(String url, int port){
 30         setPort(port);
 31         setUrl(url);
 32     }
 33 
 34     /**
 35      * 不带异常的put数据
 36      * @param key
 37      * @param value
 38      */
 39     public void putobjWithExp(String key, Object value) {
 40         Jedis jedis = null;
 41         try {
 42             jedis = getJedis();
 43             jedis.set(key, JSON.toJSONString(value));
 44         } catch (Exception e) {
 45             logger.error("获取Jedis异常", e);
 46         } finally {
 47             if (jedis != null) {
 48                 returnJedis(jedis);
 49             }
 50         }
 51     }
 52 
 53     /**
 54      * 获取jedis
 55      * @return 从池中获取jedis
 56      * @throws Exception
 57      */
 58     private Jedis getJedis() throws Exception {
 59         LoggerUtils.info(logger, "borrow jedis,borrowwed=", jedisPool.getNumActive(), ",maxTotal=",
 60             jedisPool.getMaxTotal());
 61         return jedisPool.borrowObject(maxWaitTime);
 62     }
 63 
 64     /**
 65      * 归还jedis
 66      * @param jedis
 67      */
 68     private void returnJedis(Jedis jedis) {
 69         LoggerUtils.info(logger, "return jedis,borrowwed=", jedisPool.getNumActive(), ",maxTotal=",
 70             jedisPool.getMaxTotal());
 71         jedisPool.returnObject(jedis);
 72     }
 73 
 74     /**
 75      * Setter method for property <tt>port</tt>.
 76      * 
 77      * @param port value to be assigned to property port
 78      */
 79     public void setPort(int port) {
 80         this.port = port;
 81     }
 82 
 83     /**
 84      * Setter method for property <tt>url</tt>.
 85      * 
 86      * @param url value to be assigned to property url
 87      */
 88     public void setUrl(String url) {
 89         this.url = url;
 90     }
 91 
 92     @Override
 93     public void afterPropertiesSet() throws Exception {
 94         LoggerUtils.info(logger, "开始初始化jedis池,url=", url, ",port=", port);
 95         Assert.notNull(url);
 96         GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
 97         poolConfig.setBlockWhenExhausted(true);
 98         poolConfig.setMaxWaitMillis(100);
 99         poolConfig.setLifo(false);
100         poolConfig.setMaxIdle(50);//// 最大空闲链接数
101         poolConfig.setMinIdle(20);// 最小空闲链接数
102         poolConfig.setMaxTotal(500);// 整个池的最大值,最大链接数
103         poolConfig.setTestOnBorrow(true);
104         poolConfig.setTestOnCreate(true);
105         poolConfig.setTestOnReturn(true);
106         poolConfig.setTestWhileIdle(false);
107         jedisPool = new GenericObjectPool<>(new JedisPooledFactory(url, port), poolConfig);
108     }
redis带链接池的客户端

这里jedis采用单机的形式。
首先是afterPropertiesSet方法,这里对jedis链接池作了一些配置,好比池的大小,borrow jedis链接的时候等待时间(borrow的时候采用的乐观锁),池中空闲对象超过多少的时候,return链接直接就销毁。。。等等
而后是putobjWithExp方法,这里首先是从池中borrow一个连接,若是池中没有的话,commons-pool会自动建立。而后获取到链接了之后,调用下jedis的set方法,将数据保存。json

这里采用的是fastJson来作序列化的,保存的内容也是String格式的。 而tair是支持自定义序列化工具的,并且它的序列化是

最后,将jedis链接归还到pool去就好啦。
除了对象复用之外,其实尚未提到一个很重要的使用对象池的缘由:缓存

对于链接池的场景而言,链接是有限的资源,不采用池化,那么没法对资源的分配进行管理。

打个比方,不采用链接池,每一个请求进来生成一个链接,那么若是忽然某个业务请求量递增,直接致使链接数都被该系统占用了。可是采用了链接池,不单单能够对象复用,同时还能作资源的管控。ide

测试:

测试环境:10个线程,同时采用jedispool和非jedispool的方式向redis put数据,put的数据同样,一共put 50000次,如下是测试结果。
带jedis链接池的--->任务执行完毕,执行时间5055ms,共有50000个任务,执行异常0次
不带链接池的--->任务执行完毕,执行时间14654ms,共有50000个任务,执行异常0次工具

效果仍是很明显的。后续还能够增长对链接池的定时任务监控等~~~gitlab

相关文章
相关标签/搜索