JedisPoll配置详解

本文介绍了Redis连接池JedisPool的重要性,强调了连接池能减少建立连接的时间,提高效率。文章从理论出发,解释了为何使用连接池,并通过代码示例展示了如何配置和使用JedisPool,帮助读者深入理解Redis连接池的配置含义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.首先了解一下为什么使用连接池?

        首先Redis也是一种数据库,它基于C/S模式,因此如果需要使用必须建立连接,稍微熟悉网络的人应该都清楚地知道为什么需要建立连接,C/S模式本身就是一种远程通信的交互模式,因此Redis服务器可以单独作为一个数据库服务器来独立存在。假设Redis服务器与客户端分处在异地,虽然基于内存的Redis数据库有着超高的性能,但是底层的网络通信却占用了一次数据请求的大量时间,因为每次数据交互都需要先建立连接,假设一次数据交互总共用时30ms,超高性能的Redis数据库处理数据所花的时间可能不到1ms,也即是说前期的连接占用了29ms,连接池则可以实现在客户端建立多个链接并且不释放,当需要使用连接的时候通过一定的算法获取已经建立的连接,使用完了以后则还给连接池,这就免去了数据库连接所占用的时间。

二.以上内容为百度知道片段,下面我们就来通过代码实现的方式来进一步了解redis连接池使用以及配置含义
package com.linux.util;

import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author tao
 * @des redis 连接池工具类,采用jedis2.9客户端
 */
public class RedisConnectPollUtil{
    private static final Log LOG = LogFactory.getLog(RedisConnectPollUtil.class);
    //redis获取链接的并发锁
    private static ReentrantLock redisPollLock= new ReentrantLock();
    //连接redis实例的ip
    private static final String REDIS_ADDRESS = "localhost";
    //连接redis实例的端口
    private static final int PORT = 6379;
    //多线程环境中,连接实例的最大数,如果设为-1则无上线,建议设置,否则有可能导致资源耗尽
    private static final int MAX_ACTIVE = 8;
    //在多线程环境中,连接池中最大空闲连接数,单线程环境没有实际意义
    private static final int MAX_OLDE = 4;
    //在多线程环境中,连接池中最小空闲连接数
    private static final int MIN_OLDE = 1;
    //多长时间将空闲线程进行回收,单位毫秒
    private static final int METM = 2000;
    //对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)
    private static final int SMETM = 2000;
    //逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1,只有运行了此线程,MIN_OLDE METM/SMETM才会起作用
    private static final int TBERM = 1000;
    //当连接池中连接不够用时,等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
    private static final int MAX_WAIT = 1000;
    //超时时间,单位毫秒
    private static final int TIME_OUT = 5000;
    //在借用一个jedis连接实例时,是否提前进行有效性确认操作;如果为true,则得到的jedis实例均是可用的;  
    private static final boolean TEST_ON_BORROW = false;  
    //连接池实例
    private static JedisPool jedisPool = null; 
    

    //初始化连接池,有好多重载的构造函数,根据自己业务实际需要来实例化JedisPoll
    private static void initPoll() {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_OLDE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);
            config.setMinIdle(MIN_OLDE);
//            config.setMinEvictableIdleTimeMillis(METM);
            config.setSoftMinEvictableIdleTimeMillis(SMETM);
            config.setTimeBetweenEvictionRunsMillis(TBERM);
            jedisPool = new JedisPool(config, REDIS_ADDRESS, PORT, TIME_OUT);
        } catch (Exception e) {
            LOG.error("initial JedisPoll fail:",e);
        }
        
    }
   //获取jedis连接实例
    public static Jedis getJedis() {
        redisPollLock.lock();
        if(jedisPool == null) {
            initPoll();
        }
        Jedis jedis = null;
        try {
            if(jedisPool != null) {
                jedis = jedisPool.getResource();
            }
        } catch (Exception e) {
            LOG.error("get jedis fail:",e);
        }finally {
            redisPollLock.unlock();
        }
        return jedis;
    }
    //归还jedis实例,2.9版本后jedisPool.returnResource(jedis);过期,被close替代,源码如下
    /*
         @Override
          public void close() {
            if (dataSource != null) {
              if (client.isBroken()) {
                this.dataSource.returnBrokenResource(this);
              } else {
                this.dataSource.returnResource(this);
              }
            } else {
              client.close();
            }
          }
     */
    //如果每次获取了jedis连接后不进行归还,redis不会自动回收,那么获取的最多连接数量为MAX_ACTIVE
    //超出数量则会抛出异常redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
    public static void returnSource(Jedis jedis) {
        if(jedis != null) {
            //如果连接池已经关闭了,则返回-1,最大活跃数不会超过MAX_ACTIVE,最大空闲数不会超过MAX_OLDE
            System.out.println(jedisPool.getNumWaiters()+"链接归还前活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());
            jedis.close();
            System.out.println(jedisPool.getNumWaiters()+"链接归还后活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());
        }
    }
    private static int k = 0;
    public static void main(String[] args) {
        for(int i=0;i<20;i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Jedis jedis = getJedis();
                    System.out.println("第"+(k++)+"次"+jedis.lpop("pageList"));
                    returnSource(jedis);
                    //判断此连接是否还有效,有效返回true,否则返回false
                    //连接归还后,将不可用,会抛出redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.
                    if(!jedis.isConnected()) {
                        jedis.lpop("pageList");
                    }
//                    jedisPool.close(); jedisPoll关闭后将导致池不可用
//                    System.out.println("jedispoll是否关闭了?"+jedisPool.isClosed());
                }
            }).start();
            
        }
        try {
            //主线程等待一定时间,否则会发生线程执行时效错乱问题
            Thread.currentThread().sleep(15000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(jedisPool.getNumWaiters()+"最终链接归还后活跃数:"+jedisPool.getNumActive()+"空闲连接数:"+jedisPool.getNumIdle());
        destroy();
    }
    //在运用正常运行时,通常是不会手动调用jedisPool.close();池内将保持最大空闲数的连接,如果设置了逐出策略
    //那么池内就会保留最小空闲连接,如果应用突然关闭,我们需要在bean销毁时将连接池销毁.
   
    public static void destroy(){
        if(jedisPool != null) {
            try {
                jedisPool.destroy();    
            } catch (Exception e) {
                LOG.error("jedisPool destroy fail ",e);
            }
        }    
    }

}


还有另外一种写法,这种写法跟在spring的XML中配置进行jedisPoll初始化差不多,代码如下:

package com.linux.util;

import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import com.linux.service.RedisConnectPoll;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Service("redisConnectPoll")
public class RedisConnectPollUtil2 implements InitializingBean,DisposableBean,RedisConnectPoll{
    private static final Log LOG = LogFactory.getLog(RedisConnectPollUtil.class);
    
    //连接redis实例的ip
    private static final String REDIS_ADDRESS = "localhost";
    //连接redis实例的端口
    private static final int PORT = 6379;
    //多线程环境中,连接实例的最大数,如果设为-1则无上线,建议设置,否则有可能导致资源耗尽
    private static final int MAX_ACTIVE = 8;
    //在多线程环境中,连接池中最大空闲连接数,单线程环境没有实际意义
    private static final int MAX_OLDE = 4;
    //在多线程环境中,连接池中最小空闲连接数
    private static final int MIN_OLDE = 1;
    //多长时间将空闲线程进行回收,单位毫秒
    private static final int METM = 2000;
    //对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)
    private static final int SMETM = 2000;
    //逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1,只有运行了此线程,MIN_OLDE METM/SMETM才会起作用
    private static final int TBERM = 1000;
    //当连接池中连接不够用时,等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
    private static final int MAX_WAIT = 1000;
    //超时时间,单位毫秒
    private static final int TIME_OUT = 5000;
    //在借用一个jedis连接实例时,是否提前进行有效性确认操作;如果为true,则得到的jedis实例均是可用的;  
    private static final boolean TEST_ON_BORROW = false;  
    //连接池实例
    private static JedisPool jedisPool = null; 
    
    @Override
    public void afterPropertiesSet() throws Exception {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_OLDE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);
            config.setMinIdle(MIN_OLDE);
//            config.setMinEvictableIdleTimeMillis(METM);
            config.setSoftMinEvictableIdleTimeMillis(SMETM);
            config.setTimeBetweenEvictionRunsMillis(TBERM);
            jedisPool = new JedisPool(config, REDIS_ADDRESS, PORT, TIME_OUT);
        } catch (Exception e) {
            LOG.error("initial JedisPoll fail:",e);
        }
    }
    //获取jedis连接实例
    @Override
    public Jedis getJedis() {
        if(jedisPool != null) {
            return jedisPool.getResource();
        }
        return null;
    }
   //归还jedis连接实例
    @Override
    public void returnSource(Jedis jedis) {
        if(jedis != null) {
            jedis.close();
        }
    }
    @Override
    public void destroy() throws Exception {
        if(jedisPool != null) {
            try {
                jedisPool.destroy();    
            } catch (Exception e) {
                LOG.error("jedisPool destroy fail ",e);
            }
        }    
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值