前面一篇说的是redis交给spring容器进行管理,我们使用的时候只要做好桥接即可。本篇讲述的是自定义一个简单的CacheManager,和之前的适配器一样都要实现shiro自己的缓存管理器接口CacheManager, Destroyable。
定义池管理
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
<property name="testOnReturn" value="${redis.testOnReturn}" />
</bean>
定义jediscluster
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="jedisClusterNode">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${jedis.host1}">
</constructor-arg>
<constructor-arg index="1" value="${jedis.port1}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${jedis.host2}">
</constructor-arg>
<constructor-arg index="1" value="${jedis.port2}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${jedis.host3}">
</constructor-arg>
<constructor-arg index="1" value="${jedis.port3}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${jedis.host4}">
</constructor-arg>
<constructor-arg index="1" value="${jedis.port4}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${jedis.host5}">
</constructor-arg>
<constructor-arg index="1" value="${jedis.port5}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${jedis.host6}">
</constructor-arg>
<constructor-arg index="1" value="${jedis.port6}"></constructor-arg>
</bean>
</set>
</constructor-arg>
<constructor-arg name="connectionTimeout" value="${jedis.timeout}"/>
<constructor-arg name="soTimeout" value="${jedis.soTimeout}"/>
<constructor-arg name="password" value="${jedis.password}"/>
<constructor-arg name="maxAttempts" value="${jedis.attempts}"/>
<constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>
</bean>
编写ShiroJedisCacheManager
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.Destroyable;
import redis.clients.jedis.JedisCluster;
public class ShiroJedisCacheManager implements CacheManager, Destroyable {
private static final Logger logger = LogManager.getLogger(ShiroJedisCacheManager.class);
private JedisCluster jedisCluster;
private static final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
private boolean usePrefix = false;
private String cachePrefix = null;
private long defaultExpiration = 0;
private Map<String, Long> expires = null;
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
logger.debug("Acquiring ShiroSpringCache instance named [" + name + "]");
Cache cache = this.caches.get(name);
if (cache == null) {
//自定义shiroCache
long expiration = computeExpiration(name);
cache = new ShiroJedisCache<K, V>(jedisCluster, expiration, (usePrefix ? cachePrefix : null),name);
this.caches.put(name, cache);
}
return cache;
}
public void add(String name,Cache cache) {
this.caches.put(name, cache);
}
@Override
public void destroy() throws Exception {
caches.clear();
}
public JedisCluster getJedisCluster() {
return jedisCluster;
}
public void setJedisCluster(JedisCluster jedisCluster) {
this.jedisCluster = jedisCluster;
}
public void setExpires(Map<String, Long> expires) {
this.expires = (expires != null ? new ConcurrentHashMap<String, Long>(expires) : null);
}
protected long computeExpiration(String name) {
Long expiration = null;
if (expires != null) {
expiration = expires.get(name);
}
return (expiration != null ? expiration.longValue() : defaultExpiration);
}
public boolean isUsePrefix() {
return usePrefix;
}
public void setUsePrefix(boolean usePrefix) {
this.usePrefix = usePrefix;
}
public long getDefaultExpiration() {
return defaultExpiration;
}
public void setDefaultExpiration(long defaultExpiration) {
this.defaultExpiration = defaultExpiration;
}
public String getCachePrefix() {
return cachePrefix;
}
public void setCachePrefix(String cachePrefix) {
this.cachePrefix = cachePrefix;
}
编写ShiroJedisCache
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.cache.CacheException;
import org.springframework.util.SerializationUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
@SuppressWarnings("unchecked")
public class ShiroJedisCache<K, V> implements org.apache.shiro.cache.Cache<K, V> {
private static final Logger logger = LogManager.getLogger(ShiroJedisCache.class);
private long cacheLive;
private String cacheKeyPrefix;
private JedisCluster jedisCluster;
private String name;
//优化
public ShiroJedisCache(JedisCluster jedisCluster, long cacheLive, String cachePrefix,String name) {
if (jedisCluster == null) {
throw new IllegalArgumentException("Cache argument cannot be null.");
}
this.jedisCluster = jedisCluster;
this.cacheLive = cacheLive;
this.cacheKeyPrefix = cachePrefix;
this.name=name;
}
@Override
public V get(K key) throws CacheException {
logger.debug(key);
byte[] k = SerializationUtils.serialize(key);
byte[] v = jedisCluster.get(k);
if(v==null) {
return null;
}
V value = (V) SerializationUtils.deserialize(v);
return value;
}
@Override
public V put(K key, V value) throws CacheException {
if(value==null) {
logger.debug("value is null");
return null;
}
logger.debug(key+"="+value);
V previous = get(key);
byte[] k = SerializationUtils.serialize(key);
byte[] v = SerializationUtils.serialize(value);
jedisCluster.setex(k,Integer.parseInt(String.valueOf(this.cacheLive)), v);
return previous;
}
@Override
public V remove(K key) throws CacheException {
byte[] k = SerializationUtils.serialize(key);
V v = (V) SerializationUtils.deserialize(jedisCluster.get(k));
if(v!=null) {
jedisCluster.del(k);
}
return v;
}
@Override
public void clear() throws CacheException {
Set<K> keys = keys();
for(K k:keys) {
remove(k);
}
}
@Override
public int size() {
return keys().size();
}
@Override
public Set<K> keys() {
Set<String> keys = new HashSet<String>();
Map<String, JedisPool> nodes = jedisCluster.getClusterNodes();
//遍历所有连接池,逐个进行模糊查询
for(String k : nodes.keySet()){
JedisPool pool = nodes.get(k);
//获取Jedis对象,Jedis对象支持keys模糊查询
Jedis connection = pool.getResource();
try {
keys.addAll(connection.keys(this.cacheKeyPrefix+"*"));
} catch(Exception e){
logger.error("获取key异常", e);
} finally{
logger.info("关闭连接");
connection.close();
}
}
return (Set<K>) keys;
}
@Override
public Collection<V> values() {
Set<K> keys = keys();
Set<V> values = new HashSet<V>();
for(K key:keys) {
values.add(get(key));
}
return Collections.emptySet();
}
总结,至此shiro集成redis rediscluster jediscluster代码部分的前提条件已经完成。至于大家想采用哪种方式,要看业务或者实际情况来定。适合自己的才是最好的。这里的缓存管理器不是很完整,但是已满足集群使用了。大家有兴趣的话,可以去参考spring的源码学习。
性能部分,肯定是本文说的自定义集成jediscluster是最好的,因为这是原生的,没有经过封装的接口。使用原生配置在1000并发下面,性能相差比较大,一般在5倍以上。
来源:oschina
链接:https://my.oschina.net/osokra/blog/4449161