集群部署的落地实现之spring shiro redis 架构 第二篇 shiro通过自定义CacheManager集成jedisCluster集群 redisCluster

你。 提交于 2020-08-15 08:04:13

前面一篇说的是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倍以上。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!