设计和实现:
1.引入Redis连接时的一些必要的参数:
例如IP、端口(PORT)以及权限验证密码auth等,可以通过写死在代码里,但为了后期修改方便,通常是配置在一个配置文件中,然后再运行时从配置文件中读取,这样就算需要在多处调用这些配置信息,修改时也只需修改配置文件即可。
在当前工程的src目录下新建一个配置文件,取名为Redis.properties
,其内容如下:
PORT = 6739
SERVER_ARRAY = 127.0.0.1,196.168.201.1
AUTH = 123456
TIMEOUT = 1000
MAX_ACTIVE = 8
MAX_IDLE = 8
MAX_WAIT = 2000
TEST_ON_BORROW = true
通过通过类加载目录getClassLoader()加载属性文件,这里我们也写一个文件工具类FileUtil
,内容如下:
package com.tw.login.tools;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/***
* 文件操作工具类
* @author linsh
*
*/
public class FileUtil {
/**
* 读取整数配置数据
* @param path
* @param name
* @return
*/
public static int getPropertyValueInt(String path,String name) {
int result = -1;
result = Integer.valueOf(getPropertyValueString(path,name));
return result;
}
/**
* 读取字符型配置数据
* @param path
* @param name
* @return
*/
public static String getPropertyValueString(String path,String name) {
String result = "";
// 方法二:通过类加载目录getClassLoader()加载属性文件
InputStream in = FileUtil.class.getClassLoader()
.getResourceAsStream(path);
Properties prop = new Properties();
try {
prop.load(in);
result = prop.getProperty(name).trim();
System.out.println(name ":" result);
} catch (IOException e) {
System.out.println("读取配置文件出错");
e.printStackTrace();
}
return result;
}
/**
* 读取布尔型配置数据
* @param path
* @param name
* @return
*/
public static boolean getPropertyValueBool(String path,String name) {
boolean result = false;
result = Boolean.parseBoolean(getPropertyValueString(path,name));
return result;
}
}
调用方法:
FileUtil.getPropertyValueInt("Redis.properties", "PORT")
2.创建Jedis连接池:
除了之前使用的jedis.jar包之外,我们还需要引入一个通用的池管理commons-pool.jar,记得要下载1.x的版本。
注意:
Java端在使用jedispool连接redis的时候,在高并发的时候经常卡死,或报连接异常,JedisConnectionException
或者getResource
异常等各种问题。
在使用Jedispool的时候一定要注意两点:
- 在获取 jedisPool和jedis的时候加上线程同步,保证不要创建过多的jedispool和jedis;
- 用完Jedis实例后需要返还给JedisPool;
JedisPool线程池管理类源码:
package com.tw.login.redis;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.tw.login.tools.FileUtil;
import io.netty.util.internal.StringUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/***
* Redis数据库的连接池操作类
* @author linsh
*
*/
public class RedisUtil {
//日志控制器
protected static Log logger = LogFactory.getLog(RedisUtil.class);
/**从配置文件中读取配置信息**/
//Redis服务器端口号
private static int Port = FileUtil.getPropertyValueInt("Redis.properties", "PORT");
//Redis服务器端Ip地址组(多个redis数据中心的Ip,备用)
private static String Server_address_array = FileUtil.getPropertyValueString("Redis.properties", "SERVER_ARRAY");
//Redis服务器端访问密码
private static String Auth = FileUtil.getPropertyValueString("Redis.properties", "AUTH");
//Redis连接超时时长(单位:毫秒)
private static int TimeOut = FileUtil.getPropertyValueInt("Redis.properties", "TIMEOUT");
//可用连接实例的最大数目,默认值为8,如果赋值为-1,则表示不限制;如果pool已经分配了MAX_ACTIVE个jedis实例,则此时pool的状态为exhausted(耗尽)。
private static int MAX_ACTIVE = FileUtil.getPropertyValueInt("Redis.properties", "MAX_ACTIVE");
//一个Pool最多有多少个状态为idle(空闲)的jedis实例,默认值为8个
private static int MAX_IDLE = FileUtil.getPropertyValueInt("Redis.properties", "MAX_IDLE");
//待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException异常
private static int MAX_WAIT = FileUtil.getPropertyValueInt("Redis.properties", "MAX_WAIT");
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的
private static boolean TEST_ON_BORROW = FileUtil.getPropertyValueBool("Redis.properties", "TEST_ON_BORROW");
/**
* redis数据有效时长
*/
public final static int EXRP_HOUR = 60*60; //1小时
public final static int EXRP_DAY = 60*60*24; //1天
public final static int EXRP_MOUTH = 60*60*24*30; //1个月
//Jedis连接池
private static JedisPool jedisPool = null;
/**
* 初始化连接池
*/
private static void InitPoolConfig() {
//假如第一个ip的redis访问异常,则选用第二个
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxActive(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWait((long)MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, Server_address_array.split(",")[0],Port,TimeOut);
} catch (Exception e) {
logger.error("first redis server fail:" e);
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxActive(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWait((long)MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, Server_address_array.split(",")[1],Port,TimeOut);
} catch (Exception e2) {
logger.error("add redis servers fail:" e);
}
}
}
/**
* 在多线程环境下,避免重复初始化
*/
private static synchronized void PoolInit() {
if(jedisPool == null){
InitPoolConfig();
}
}
/**
* 同步获取Jedis实例
* @return
*/
public synchronized static Jedis getJedis() {
if(jedisPool == null){
InitPoolConfig();
}
Jedis jedis = null;
try {
if(jedisPool!=null){
jedis = jedisPool.getResource();
}
} catch (Exception e) {
logger.error("Get redis fail:" e);
}finally {
returnResource(jedis);
}
return jedis;
}
/**
* 释放Jedis资源
* @param jedis
*/
private static void returnResource(Jedis jedis) {
if(jedis!=null&&jedisPool!=null){
jedisPool.returnBrokenResource(jedis);
}
}
/**
* 设置 String
* @param key
* @param value
*/
public static void setString(String key ,String value){
try {
value = StringUtil.isNullOrEmpty(value) ? "" : value;
getJedis().set(key,value);
} catch (Exception e) {
logger.error("Set key error : " e);
}
}
/**
* 设置 过期时间
* @param key
* @param seconds 以秒为单位
* @param value
*/
public static void setString(String key ,int seconds,String value){
try {
value = StringUtil.isNullOrEmpty(value) ? "" : value;
getJedis().setex(key, seconds, value);
} catch (Exception e) {
logger.error("Set keyex error : " e);
}
}
/**
* 获取String值
* @param key
* @return value
*/
public static String getString(String key){
if(getJedis() == null || !getJedis().exists(key)){
return null;
}
return getJedis().get(key);
}
}
3.使用案例:
直接将之前使用单独创建Jedis实例进行Redis操作的代码:
//连接redis服务器,127.0.0.1:6379
Jedis jedis = new Jedis("127.0.0.1",6379);
//权限认证
jedis.auth("123456");
jedis.set("IdNum", "123456");
替换为:
Jedis jedis = RedisUtil.getJedis();
jedis.set("IdNum", "123456");
或者替换为RedisUtil中封装的静态方法:
RedisUtil.setString("IdNum", "123456");