redis服務模塊封裝
redis單節點緩存數據庫操作分裝類;這篇博客對應上一篇博客CentOS7-源碼安裝redis單節點。此處,繼上次安裝好的redis之後,通過Java訪問redis,並將redis操作封裝成工具類。
包結構
其中:
——測試類:RedisTest
——服務類:RedisService
——工具類:RedisUtil
——配置文件:redis.properties
——配置文件備份:redis.bak
採用架構:Spring MVC
redis所需架包:
其中,被選中的*.src包爲源碼包,發佈的應用中不需要。
說明:項目僅引用redis的所需的包會報一個錯,logger日誌的所用類會找不到。源碼是logger日誌這裏用的是log4j的包,被整合在SpringMVC架構包中。解決方式可以將log4j對應的包導入,或者將logger的打印信息直接註釋,這也不影響redis服務類的使用。
配置文件信息
#Redis服務器IP
ADDR = 10.0.0.6
#Redis的端口號
PORT = 6379
#訪問密碼
AUTH = anson
#可用連接實例的最大數目,默認值爲8;
MAX_ACTIVE = 500
#控制一個pool最多有多少個狀態爲空閒的
MAX_IDLE = 500
#等待可用連接的最大時間,單位毫秒
MAX_WAIT = 10000
TIMEOUT = 10000
#在獲取一個jedis實例時,是否提前進行可用性判斷
TEST_ON_BORROW = true
RedisUtil工具類
package com.esh.redis;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author Anson
* Copyright by EasyShare 2017
*
* All right reserved
*
* Created on 2017年6月25日 下午4:12:56
*
* redis工具類
*/
public final class RedisUtil {
//Redis服務器IP
private static String ADDR;
//Redis的端口號
private static int PORT;
//訪問密碼
private static String AUTH;
//可用連接實例的最大數目,默認值爲8;
private static int MAX_ACTIVE;
//控制一個pool最多有多少個狀態爲空閒的,默認值也是8。
private static int MAX_IDLE;
//等待可用連接的最大時間,單位毫秒,默認值爲-1,表示永不超時。
private static int MAX_WAIT;
private static int TIMEOUT;
//在獲取一個jedis實例時,是否提前進行可用性判斷;如果爲true,則得到的jedis實例均是可用的;
private static boolean TEST_ON_BORROW;
private static JedisPool jedisPool = null;
private static Logger logger=LoggerFactory.getLogger(RedisUtil.class.getName());
/**
* 初始化Redis連接池
*/
static
{
try
{
//加載參數
loadProperty();
} catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 加載redis配置參數
*/
private final static void loadProperty() {
Properties prop=new Properties();
try {
prop.load(new FileInputStream(new File(Thread.currentThread().getContextClassLoader().getResource("").getPath()+"redis.properties")));
ADDR=prop.getProperty("ADDR").trim();
PORT=Integer.parseInt(prop.getProperty("PORT").trim());
AUTH=prop.getProperty("AUTH");
MAX_ACTIVE=Integer.parseInt(prop.getProperty("MAX_ACTIVE").trim());
MAX_IDLE=Integer.parseInt(prop.getProperty("MAX_IDLE").trim());
MAX_WAIT=Integer.parseInt(prop.getProperty("MAX_WAIT").trim());
TIMEOUT=Integer.parseInt(prop.getProperty("TIMEOUT").trim());
TEST_ON_BORROW=prop.getProperty("TEST_ON_BORROW").trim().toLowerCase().equals("true");
logger.info("redis參數加載成功,"
+ "參數:ADDR="+ADDR+" PORT="+PORT+" AUTH="+AUTH+
"MAX_ACTIVE="+MAX_ACTIVE+" MAX_IDLE="+MAX_IDLE+" MAX_WAIT="+MAX_WAIT+" TIMEOUT="+TIMEOUT+" TEST_ON_BORROW="+TEST_ON_BORROW);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 創建redis池
*/
private final static void initJedisPool() {
try
{
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
// logger.info(jedisPool!=null?"redis池創建成功":"redis池創建失敗,位置:"+RedisUtil.class.getName());
logger.info(jedisPool!=null?"redis連接池創建成功":"");
}catch(Exception e)
{
logger.error("第一次嘗試創建jedis連接池錯誤,位置:"+RedisUtil.class.getName());
try
{
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
logger.info(jedisPool!=null?"redis連接池創建成功":"");
}catch(Exception e1)
{
logger.error("第二次嘗試創建jedis連接池錯誤,位置:"+RedisUtil.class.getName());
}
}
}
/**
* 多線程下同步初始化
*/
private synchronized static void loadPool()
{
if(jedisPool==null)
{
initJedisPool();
}
}
/**
* 同步獲取Jedis實例
* @return
*/
public synchronized static Jedis getJedis(){
if(jedisPool==null)
{
//未創建連接池是創建連接池
loadPool();
}
Jedis jedis=null;
try
{
if (jedisPool != null)
{
jedis= jedisPool.getResource();
}
} catch (Exception e)
{
logger.info("獲取redis對象失敗,位置:"+RedisUtil.class.getName());
e.printStackTrace();
}
return jedis;
}
/**
* 釋放jedis資源
* @param jedis
*/
public synchronized static void close(final Jedis jedis){
if (jedis!=null)
{
jedis.close();
}
}
}
package com.esh.redis;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
/**
* @author Anson
* Copyright by EasyShare 2017
*
* All right reserved
*
* Created on 2017年6月25日 下午4:12:38
*
* redis服務類
*/
public class RedisService {
private static Logger logger=LoggerFactory.getLogger(RedisService.class.getName());
/**
* 通過key值獲取redis中的value,獲取後釋放鏈接
* @param key
* @return
* object
*/
public synchronized static Object get(String key)
{
Jedis jedis=null;
byte[] value=null;
Object obj=null;
jedis=RedisUtil.getJedis();
value=jedis.get(key.getBytes());
if(value!=null)
{
obj=deSerialize(value);
}
RedisUtil.close(jedis);
logger.info(obj!=null?"獲取緩存成功,":"獲取緩存數據失敗,位置:"+RedisService.class.getName());
return obj;
}
/**
* 緩存一個對象,key存在則覆蓋
* @param key
* @param value
* @return
* boolean
*/
public synchronized static boolean set(String key, Object obj)
{
Jedis jedis=null;
String code=null;
jedis=RedisUtil.getJedis();
code=jedis.set(key.getBytes(), serialize(obj));
RedisUtil.close(jedis);
logger.info(code.toLowerCase().equals("ok")?"緩存成功":"數據緩存失敗,位置:"+RedisService.class.getName());
return code.toLowerCase().equals("ok");
}
/**
* 緩存一個鍵值對,key-value
* @param key
* @param value
* @return
* boolean
*/
public synchronized static boolean set(String key, String value)
{
Jedis jedis=null;
String code=null;
jedis=RedisUtil.getJedis();
code=jedis.set(key.getBytes(), serialize(value));
RedisUtil.close(jedis);
logger.info(code.toLowerCase().equals("ok")?"緩存成功":"數據緩存失敗,位置:"+RedisService.class.getName());
return code.toLowerCase().equals("ok");
}
/**
* redis批量寫入
* 事物操作
* 當前進行的事物操作,若對應的數據被其他進程修改,則該事物將被打斷
* @param objects
* Map<String, Object>
* @return
*/
public synchronized static boolean tset(Map<String, Object> objects)
{
Jedis jedis=null;
List<Object> result=null;
jedis=RedisUtil.getJedis();
Transaction t=null;
try {
t=jedis.multi();
for(Entry<String, Object> entry:objects.entrySet())
{
//監視數據
t.watch(entry.getKey().getBytes());
//保存數據
t.set(entry.getKey().getBytes(), serialize(entry.getValue()));
}
result=t.exec();
} catch (Exception e) {
//回滾操作
t.discard();
logger.error("redis事物存儲失敗,位置:"+RedisService.class.getName());
}
RedisUtil.close(jedis);
return result!=null;
}
/**
* 刪除key對應的緩存數據
* @param key
* @return
* boolean
*/
public synchronized static long del(String ... keys)
{
Jedis jedis=null;
long code=0;
jedis=RedisUtil.getJedis();
code=jedis.del(keys);
RedisUtil.close(jedis);
logger.info(code>1?"刪除成功":"緩存數據刪除失敗,位置:"+RedisService.class.getName());
return code;
}
/**
* 批量刪除操作
* @param key
* @return
*/
public synchronized static long delbat(String key)
{
Jedis jedis=null;
long code=0;
jedis=RedisUtil.getJedis();
Set<String> keySet=jedis.keys(key+"*");
if(keySet!=null&&keySet.size()>0)
{
//將獲得的keys轉換成數組類型
String[] keys=new String[keySet.size()];
keySet.toArray(keys);
code=jedis.del(keys);
}
RedisUtil.close(jedis);
return code;
}
/**
* 清空數據庫
* @param isClearAll
* 是否清空所有數據庫
* false-清空當前使用的數據庫,默認爲0
* @return
* true||false
*/
public synchronized static boolean clear(boolean isClearAll)
{
Jedis jedis=null;
String code=null;
jedis=RedisUtil.getJedis();
if(isClearAll)
{
code=jedis.flushAll();
logger.info(code.toLowerCase().equals("ok")?"清空所有緩存數據庫成功":"清空所有緩存數據庫失敗");
}else
{
code = jedis.flushDB();
logger.info(code.toLowerCase().equals("ok")?"清空當前緩存數據庫清空成功":"清空當前緩存數據庫失敗");
}
RedisUtil.close(jedis);
return code.toLowerCase().equals("ok");
}
/**
* 批量獲取操作
* @param key
* @return
*/
public synchronized static List<Object> getbat(String key)
{
Jedis jedis=null;
List<Object> objects=new ArrayList<Object>();
Object object=null;
jedis=RedisUtil.getJedis();
Set<String> keySet=jedis.keys(key+"*");
RedisUtil.close(jedis);
if(keySet!=null&&keySet.size()>0)
{
Iterator<String> it=keySet.iterator();
while(it.hasNext())
{
String item=it.next();
object=get(item);
if(object!=null)
{
objects.add(object);
}
}
}
return objects;
}
/**
* 序列化存儲對象
* @param obj
* @return
*/
private synchronized static byte[] serialize(Object obj)
{
byte[] serialObj=null;
ObjectOutputStream oos=null;
ByteArrayOutputStream baos=null;
try {
baos=new ByteArrayOutputStream();
oos=new ObjectOutputStream(baos);
oos.writeObject(obj);
serialObj=baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}finally
{
try {
if(baos!=null)
{
baos.close();
}
if(oos!=null)
{
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
logger.error("釋放序列化資源失敗,位置:"+RedisService.class.getName());
}
}
return serialObj;
}
/**
* 反序列化對象
* @param serialObj
* @return
*/
private synchronized static Object deSerialize(byte[] serialObj)
{
Object object=null;
ObjectInputStream ois=null;
ByteArrayInputStream bais=null;
try {
if(serialObj!=null&&serialObj.length>0)
{
bais=new ByteArrayInputStream(serialObj);
ois=new ObjectInputStream(bais);
object=ois.readObject();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally
{
try {
if(bais!=null)
{
bais.close();
}
if(ois!=null)
{
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
logger.error("釋放反序列化資源失敗,位置:"+RedisService.class.getName());
}
}
return object;
}
}
測試類RedisTest代碼就不貼了,有興趣的自己copy源碼測試一波,博主自己在多線程環境下測試過所有服務功能,均可用,如有發現問題,亦可給我留言。
博主的測試環境是:
——redis單節點位置:CentOS7.2-x86-64;
——測試環境:Win7-x86-64,eclipse,jdk8,JMeter;
——測試最大並行線程量:8000;
——已發現並修正的問題:釋放jedis資源時,多線程下報錯;
另注意,如果redis數據庫節點位置與測試所用PC不在統一網絡上,需確保兩臺計算機之間通信穩定。