redis是作为缓存一个非常好的解决方案,但是redis也相当于一个数据库,其在同一时间的可用连接是有限的,在高并发下必须使用redis连接池来确保服务器内存安全以及数据能正确正常地存储到redis中.
同时,使用redis连接池的好处有:
1.复用连接,减少建立/释放连接的消耗
2.确保数据能正确写入redis缓存中
代码如下:
public class JedisUtil {
private static Logger logger = LoggerFactory.getLogger(JedisUtil.class);
private static String redisHost = Config.getRedisHost();
private static int redisPort = Config.getRedisPort();
private static int redisDB = Config.getRedisDB();
private static String auth = Config.getRedisPassword();
/**redis连接池设置**/
private static int MAX_POOL_SIZE = Config.getRedisPoolSize();
private static JedisPool pool;
/**缓存的过期时间**/
private static int CACHE_TIMEOUT = 3;
public static String getRedisHost(){
return redisHost;
}
public static int getRedisPort(){
return redisPort;
}
public static int getRedisDB(){
return redisDB;
}
public static String getAuth(){
return auth;
}
/**
* 初始化连接池,加上同步,避免多线程下重复初始化
*/
public static synchronized void initJedisPool(){
//static修饰的代码会比实例对象更快执行
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(MAX_POOL_SIZE);
pool = new JedisPool(jedisPoolConfig, redisHost, redisPort);
}
/**
* 从连接池中获取jedis实例
* @return
*/
public static Jedis getJedisInstance(){
if (pool == null) initJedisPool();
return pool.getResource();
}
/**
* 从redis获取键为key的值
* @param key 键
* @return redis中键为key的value
*/
public static String getString(String key){
String value = null;
Jedis jedis = null;
try{
jedis = getJedisInstance();
value = jedis.get(key);
} catch (NoSuchElementException noe){
logger.warn("jedis instance in JedisPool has been Exhausted");
noe.printStackTrace();
} catch (JedisConnectionException jce){
logger.warn("jedis instance in JedisPool has been Exhausted and wait for timeout");
jce.printStackTrace();
} finally {
if (jedis != null) jedis.close();
}
return value;
}
/**
* 将键值对(key, value)存进redis,并设置过期时间为timeout,单位是ms
* @param key key
* @param value value
* @param timeout 过期时间
*/
public static void setString(String key, String value, int timeout){
Jedis jedis = null;
try{
jedis = getJedisInstance();
jedis.set(key,value);
jedis.expire(key, timeout);
}catch (NoSuchElementException noe){
logger.warn("jedis instance in JedisPool has been Exhausted");
noe.printStackTrace();
} catch (JedisConnectionException jce){
logger.warn("jedis instance in JedisPool has been Exhausted and wait for timeout");
jce.printStackTrace();
} finally {
if (jedis != null) jedis.close();
}
}
/**
* 使用默认的过期时间存储键值对
* @param key key
* @param value value
*/
public static void setString(String key, String value){
setString(key, value, CACHE_TIMEOUT);
}
/**
* 检查key是否存在
* @param key key
* @return boolean
*/
public static boolean containsKey(String key){
Jedis jedis = null;
try{
jedis = getJedisInstance();
return jedis.exists(key);
} catch (NoSuchElementException noe){
logger.warn("jedis instance in JedisPool has been Exhausted");
noe.printStackTrace();
} catch (JedisConnectionException jce){
logger.warn("jedis instance in JedisPool has been Exhausted and wait for timeout");
jce.printStackTrace();
} finally {
if (jedis != null) jedis.close();
}
return false;
}
public static void removeKey(String key){
Jedis jedis = null;
try{
jedis = getJedisInstance();
jedis.del(key);
} catch (NoSuchElementException noe){
logger.warn("jedis instance in JedisPool has been Exhausted");
noe.printStackTrace();
} catch (JedisConnectionException jce){
logger.warn("jedis instance in JedisPool has been Exhausted and wait for timeout");
jce.printStackTrace();
} finally {
if (jedis != null) jedis.close();
}
}
/**
* 关闭redis连接池
*/
public static void close(){
if (pool != null) pool.close();
}
}
关键点有以下几点:
1.Jedis实例就相当于一个redis的连接,因此,每次进行操作使用的jedis对象实例必须是从JedisPool中获取的,这样才能复用连接
2.每次使用jedis实例对象后,都必须调用jedis.close()将redis连接归还到连接池,高版本的jedis直接调用close()就能归还连接,低版本需要调用其他方法
3.每次获取jedis实例对象不一定会成功,当连接耗尽后会抛出
NoSuchElementException,这就必须捕获这个异常,要么阻塞线程,要么丢弃这次操作,由于radius协议是基于udp的,可靠性要求比较低,所以我的redis连接池就直接丢弃这个操作了.