package com.jarvis.cache.redis;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jarvis.cache.AbstractCacheManager;
import com.jarvis.cache.exception.CacheCenterConnectionException;
import com.jarvis.cache.script.AbstractScriptParser;
import com.jarvis.cache.serializer.ISerializer;
import com.jarvis.cache.serializer.StringSerializer;
import com.jarvis.cache.to.AutoLoadConfig;
import com.jarvis.cache.to.CacheKeyTO;
import com.jarvis.cache.to.CacheWrapper;
import redis.clients.jedis.JedisCluster;
/**
* Redis缓存管理
* @author jiayu.qiu
*/
public class JedisClusterCacheManager extends AbstractCacheManager {
private static final Logger logger=LoggerFactory.getLogger(JedisClusterCacheManager.class);
private static final StringSerializer keySerializer=new StringSerializer();
private JedisCluster jedisCluster;
/**
* Hash的缓存时长:等于0时永久缓存;大于0时,主要是为了防止一些已经不用的缓存占用内存;hashExpire小于0时,则使用@Cache中设置的expire值(默认值为-1)。
*/
private int hashExpire=-1;
/**
* 是否通过脚本来设置 Hash的缓存时长
*/
private boolean hashExpireByScript=true;
public JedisClusterCacheManager(AutoLoadConfig config, ISerializer<Object> serializer, AbstractScriptParser scriptParser, JedisCluster jedisCluster) {
super(config, serializer, scriptParser);
this.jedisCluster=jedisCluster;
}
public void setJedisCluster(JedisCluster jedisCluster) {
this.jedisCluster=jedisCluster;
}
@Override
public void setCache(final CacheKeyTO cacheKeyTO, final CacheWrapper<Object> result, final Method method, final Object args[]) throws CacheCenterConnectionException {
if(null == jedisCluster || null == cacheKeyTO) {
return;
}
String cacheKey=cacheKeyTO.getCacheKey();
if(null == cacheKey || cacheKey.length() == 0) {
return;
}
try {
int expire=result.getExpire();
String hfield=cacheKeyTO.getHfield();
if(null == hfield || hfield.length() == 0) {
if(expire == 0) {
jedisCluster.set(keySerializer.serialize(cacheKey), getSerializer().serialize(result));
} else if(expire > 0) {
jedisCluster.setex(keySerializer.serialize(cacheKey), expire, getSerializer().serialize(result));
}
} else {
hashSet(cacheKey, hfield, result);
}
} catch(Exception ex) {
logger.error(ex.getMessage(), ex);
} finally {
}
}
private static byte[] hashSetScript;
static {
try {
String tmpScript="redis.call('HSET', KEYS[1], ARGV[1], ARGV[2]);\nredis.call('EXPIRE', KEYS[1], tonumber(ARGV[3]));";
hashSetScript=tmpScript.getBytes("UTF-8");
} catch(UnsupportedEncodingException ex) {
logger.error(ex.getMessage(), ex);
}
}
private void hashSet(String cacheKey, String hfield, CacheWrapper<Object> result) throws Exception {
byte[] key=keySerializer.serialize(cacheKey);
byte[] field=keySerializer.serialize(hfield);
byte[] val=getSerializer().serialize(result);
int hExpire;
if(hashExpire < 0) {
hExpire=result.getExpire();
} else {
hExpire=hashExpire;
}
if(hExpire == 0) {
jedisCluster.hset(key, field, val);
} else if(hExpire > 0) {
if(hashExpireByScript) {
List<byte[]> keys=new ArrayList<byte[]>();
keys.add(key);
List<byte[]> args=new ArrayList<byte[]>();
args.add(field);
args.add(val);
args.add(keySerializer.serialize(String.valueOf(hExpire)));
jedisCluster.eval(hashSetScript, keys, args);
} else {
jedisCluster.hset(key, field, val);
jedisCluster.expire(key, hExpire);
}
}
}
@SuppressWarnings("unchecked")
@Override
public CacheWrapper<Object> get(final CacheKeyTO cacheKeyTO, final Method method, final Object args[]) throws CacheCenterConnectionException {
if(null == jedisCluster || null == cacheKeyTO) {
return null;
}
String cacheKey=cacheKeyTO.getCacheKey();
if(null == cacheKey || cacheKey.length() == 0) {
return null;
}
CacheWrapper<Object> res=null;
try {
byte bytes[]=null;
String hfield=cacheKeyTO.getHfield();
if(null == hfield || hfield.length() == 0) {
bytes=jedisCluster.get(keySerializer.serialize(cacheKey));
} else {
bytes=jedisCluster.hget(keySerializer.serialize(cacheKey), keySerializer.serialize(hfield));
}
Type returnType=method.getGenericReturnType();
res=(CacheWrapper<Object>)getSerializer().deserialize(bytes, returnType);
} catch(Exception ex) {
logger.error(ex.getMessage(), ex);
} finally {
}
return res;
}
/**
* 根据缓存Key删除缓存
* @param cacheKeyTO 缓存Key
*/
@Override
public void delete(CacheKeyTO cacheKeyTO) throws CacheCenterConnectionException {
if(null == jedisCluster || null == cacheKeyTO) {
return;
}
String cacheKey=cacheKeyTO.getCacheKey();
if(null == cacheKey || cacheKey.length() == 0) {
return;
}
logger.debug("delete cache:" + cacheKey);
try {
String hfield=cacheKeyTO.getHfield();
if(null == hfield || hfield.length() == 0) {
jedisCluster.del(keySerializer.serialize(cacheKey));
} else {
jedisCluster.hdel(keySerializer.serialize(cacheKey), keySerializer.serialize(hfield));
}
this.getAutoLoadHandler().resetAutoLoadLastLoadTime(cacheKeyTO);
} catch(Exception ex) {
logger.error(ex.getMessage(), ex);
} finally {
}
}
public JedisCluster getJedisCluster() {
return jedisCluster;
}
public int getHashExpire() {
return hashExpire;
}
public void setHashExpire(int hashExpire) {
if(hashExpire < 0) {
return;
}
this.hashExpire=hashExpire;
}
public boolean isHashExpireByScript() {
return hashExpireByScript;
}
public void setHashExpireByScript(boolean hashExpireByScript) {
this.hashExpireByScript=hashExpireByScript;
}
}