Spring Bootキャッシュ実戦Redis設定有効時間と自動リフレッシュキャッシュ-2

12568 ワード

に質問
前のSpring Boot Cache+redisは有効時間と自動キャッシュを設定して、時間はプロファイルの中で配置することを支持して、1種の時間の方式を言って、直接注釈のValue値を拡張して、例えば:
@Override
@Cacheable(value = "people#${select.cache.timeout:1800}#${select.cache.refresh:600}", key = "#person.id", sync = true)
public Person findOne(Person person, String a, String[] b, List c) {
    Person p = personRepository.findOne(person.getId());
    System.out.println(" id、key :" + p.getId() + "      ");
    System.out.println(redisTemplate);
    return p;
}

しかし、この方式には既存のSpring Cacheアーキテクチャを破壊し、後期にキャッシュを交換したい場合に多くのコードを変更する弊害がある.
解決策
RedisCacheManagerでは、CacheManagerのBeanを構成するときに、次のような期限切れを指定できます.
@Bean
public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
    RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
    //           key  
    redisCacheManager.setUsePrefix(true);
    //                    
    redisCacheManager.setDefaultExpiration(redisDefaultExpiration);

    //          
    Map expires = new HashMap<>();
    expires.put("people", 1000);
    redisCacheManager.setExpires(expires);

    return redisCacheManager;
}

RedisCacheManagerを参考にしてみましょうsetExpires(expires)構想を拡張します.CacheTimeクラスを直接新規作成して、有効期限と自動リフレッシュ時間を保存します.
RedisCacheManagerがgetCache(name)を呼び出してキャッシュを取得する場合、キャッシュが見つからない場合はgetMissingCache(String cacheName)を呼び出してキャッシュを新規作成します.新しいキャッシュを作成するときは、拡張されたMap cacheTimesでkeyに基づいてCacheTimeを取得し、有効時間と自動リフレッシュ時間を取得できます.
具体的な実装
まずCacheTimeクラスを新規作成します
CacheTime:
 /**
 * @author yuhao.wang
 */
public class CacheTime {
    public CacheTime(long preloadSecondTime, long expirationSecondTime) {
        this.preloadSecondTime = preloadSecondTime;
        this.expirationSecondTime = expirationSecondTime;
    }

    /**
     *                  
     *   : 
     */
    private long preloadSecondTime = 0;

    /**
     *       
     */
    private long expirationSecondTime;

    public long getPreloadSecondTime() {
        return preloadSecondTime;
    }

    public long getExpirationSecondTime() {
        return expirationSecondTime;
    }
}

RedisCacheクラスを拡張
前編のCustomizedRedisCacheクラスと同様に、主に解決します.
  • キャッシュの大同時下のバグを取得し、詳細
  • .
  • キャッシュを取得する際にキャッシュの有効期限と自動リフレッシュ時間を判断し、この値に基づいてキャッシュ
  • をリフレッシュする.
    CustomizedRedisCache:
    /**
     *     redis  
     *
     * @author yuhao.wang
     */
    public class CustomizedRedisCache extends RedisCache {
    
        private static final Logger logger = LoggerFactory.getLogger(CustomizedRedisCache.class);
    
        private CacheSupport getCacheSupport() {
            return SpringContextUtils.getBean(CacheSupport.class);
        }
    
        private final RedisOperations redisOperations;
    
        private final byte[] prefix;
    
        /**
         *                  
         *   : 
         */
        private long preloadSecondTime = 0;
    
        /**
         *       
         */
        private long expirationSecondTime;
    
        public CustomizedRedisCache(String name, byte[] prefix, RedisOperations extends Object, ? extends Object> redisOperations, long expiration, long preloadSecondTime) {
            super(name, prefix, redisOperations, expiration);
            this.redisOperations = redisOperations;
            //       
            this.expirationSecondTime = expiration;
            //         
            this.preloadSecondTime = preloadSecondTime;
            this.prefix = prefix;
        }
    
        public CustomizedRedisCache(String name, byte[] prefix, RedisOperations extends Object, ? extends Object> redisOperations, long expiration, long preloadSecondTime, boolean allowNullValues) {
            super(name, prefix, redisOperations, expiration, allowNullValues);
            this.redisOperations = redisOperations;
            //       
            this.expirationSecondTime = expiration;
            //         
            this.preloadSecondTime = preloadSecondTime;
            this.prefix = prefix;
    
        }
    
        /**
         *   get  ,                ,                      。
         *      get   ,             。
         *              。
         *
         * @param key
         * @return
         */
        @Override
        public ValueWrapper get(final Object key) {
            RedisCacheKey cacheKey = getRedisCacheKey(key);
            String cacheKeyStr = getCacheKey(key);
            //       get  
            ValueWrapper valueWrapper = this.get(cacheKey);
    
            if (null != valueWrapper) {
                //       
                refreshCache(key, cacheKeyStr);
            }
            return valueWrapper;
        }
    
        /**
         *      get  。
         *    get  ,    exists  key    ,     null,    redis      。         ,
         *           exists    key  ,              ,      。
         *                    null 。
         *          ,    key    。
         *
         * @param cacheKey
         * @return
         */
        @Override
        public RedisCacheElement get(final RedisCacheKey cacheKey) {
    
            Assert.notNull(cacheKey, "CacheKey must not be null!");
    
            //   key     
            RedisCacheElement redisCacheElement = new RedisCacheElement(cacheKey, fromStoreValue(lookup(cacheKey)));
            //   key    
            Boolean exists = (Boolean) redisOperations.execute(new RedisCallback() {
    
                @Override
                public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                    return connection.exists(cacheKey.getKeyBytes());
                }
            });
    
            if (!exists.booleanValue()) {
                return null;
            }
    
            return redisCacheElement;
        }
    
        /**
         *       
         */
        private void refreshCache(Object key, String cacheKeyStr) {
            Long ttl = this.redisOperations.getExpire(cacheKeyStr);
            if (null != ttl && ttl <= CustomizedRedisCache.this.preloadSecondTime) {
                //          ,         
                ThreadTaskUtils.run(new Runnable() {
                    @Override
                    public void run() {
                        //        ,           
                        RedisLock redisLock = new RedisLock((RedisTemplate) redisOperations, cacheKeyStr + "_lock");
                        try {
                            if (redisLock.lock()) {
                                //               ,         
                                Long ttl = CustomizedRedisCache.this.redisOperations.getExpire(cacheKeyStr);
                                if (null != ttl && ttl <= CustomizedRedisCache.this.preloadSecondTime) {
                                    //                   
                                    CustomizedRedisCache.this.getCacheSupport().refreshCacheByKey(CustomizedRedisCache.super.getName(), cacheKeyStr);
                                }
                            }
                        } catch (Exception e) {
                            logger.info(e.getMessage(), e);
                        } finally {
                            redisLock.unlock();
                        }
                    }
                });
            }
        }
    
        public long getExpirationSecondTime() {
            return expirationSecondTime;
        }
    
    
        /**
         *   RedisCacheKey
         *
         * @param key
         * @return
         */
        public RedisCacheKey getRedisCacheKey(Object key) {
    
            return new RedisCacheKey(key).usePrefix(this.prefix)
                    .withKeySerializer(redisOperations.getKeySerializer());
        }
    
        /**
         *   RedisCacheKey
         *
         * @param key
         * @return
         */
        public String getCacheKey(Object key) {
            return new String(getRedisCacheKey(key).getKeyBytes());
        }
    }
    

    拡張RedisCacheManager
    プライマリ拡張がgetCache(String name)メソッドでキャッシュを取得する場合、キャッシュが見つからない場合はgetMissingCache(String cacheName)を呼び出してキャッシュを新規作成します.
    CustomizedRedisCacheManager:
    /**
     *     redis     
     *            
     *        :             
     *
     * @author yuhao.wang
     */
    public class CustomizedRedisCacheManager extends RedisCacheManager {
    
        private static final Logger logger = LoggerFactory.getLogger(CustomizedRedisCacheManager.class);
    
        /**
         *   dynamic  
         */
        private static final String SUPER_FIELD_DYNAMIC = "dynamic";
    
        /**
         *   cacheNullValues  
         */
        private static final String SUPER_FIELD_CACHENULLVALUES = "cacheNullValues";
    
        RedisCacheManager redisCacheManager = null;
    
        // 0 - never expire
        private long defaultExpiration = 0;
        private Map cacheTimes = null;
    
        public CustomizedRedisCacheManager(RedisOperations redisOperations) {
            super(redisOperations);
        }
    
        public CustomizedRedisCacheManager(RedisOperations redisOperations, Collection cacheNames) {
            super(redisOperations, cacheNames);
        }
    
        public RedisCacheManager getInstance() {
            if (redisCacheManager == null) {
                redisCacheManager = SpringContextUtils.getBean(RedisCacheManager.class);
            }
            return redisCacheManager;
        }
    
        /**
         *       
         *
         * @return
         */
        public long getExpirationSecondTime(String name) {
            if (StringUtils.isEmpty(name)) {
                return 0;
            }
    
            CacheTime cacheTime = null;
            if (!CollectionUtils.isEmpty(cacheTimes)) {
                cacheTime = cacheTimes.get(name);
            }
            Long expiration = cacheTime != null ? cacheTime.getExpirationSecondTime() : defaultExpiration;
            return expiration < 0 ? 0 : expiration;
        }
    
        /**
         *         
         *
         * @return
         */
        private long getPreloadSecondTime(String name) {
            //       ,   0
            CacheTime cacheTime = null;
            if (!CollectionUtils.isEmpty(cacheTimes)) {
                cacheTime = cacheTimes.get(name);
            }
            Long preloadSecondTime = cacheTime != null ? cacheTime.getPreloadSecondTime() : 0;
            return preloadSecondTime < 0 ? 0 : preloadSecondTime;
        }
    
        /**
         *     
         *
         * @param cacheName     
         * @return
         */
        public CustomizedRedisCache getMissingCache(String cacheName) {
    
            //     ,            
            Long expirationSecondTime = getExpirationSecondTime(cacheName);
            //       ,   0
            Long preloadSecondTime = getPreloadSecondTime(cacheName);
    
            logger.info("   cacheName:{},    :{},       :{}", cacheName, expirationSecondTime, preloadSecondTime);
            //         Cache
            Boolean dynamic = (Boolean) ReflectionUtils.getFieldValue(getInstance(), SUPER_FIELD_DYNAMIC);
            //       NULL
            Boolean cacheNullValues = (Boolean) ReflectionUtils.getFieldValue(getInstance(), SUPER_FIELD_CACHENULLVALUES);
            return dynamic ? new CustomizedRedisCache(cacheName, (this.isUsePrefix() ? this.getCachePrefix().prefix(cacheName) : null),
                    this.getRedisOperations(), expirationSecondTime, preloadSecondTime, cacheNullValues) : null;
        }
    
        /**
         *                     ,   
         *
         * @param cacheTimes
         */
        public void setCacheTimess(Map cacheTimes) {
            this.cacheTimes = (cacheTimes != null ? new ConcurrentHashMap(cacheTimes) : null);
        }
    
        /**
         *          ,   : 
         *
         * @param defaultExpireTime
         */
        @Override
        public void setDefaultExpiration(long defaultExpireTime) {
            super.setDefaultExpiration(defaultExpireTime);
            this.defaultExpiration = defaultExpireTime;
        }
    
        @Deprecated
        @Override
        public void setExpires(Map expires) {
    
        }
    }
    

    ConfigでのRedisCacheManagerの構成
    @Bean
    public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
        CustomizedRedisCacheManager redisCacheManager = new CustomizedRedisCacheManager(redisTemplate);
        //           key  
        redisCacheManager.setUsePrefix(true);
        //                    
        redisCacheManager.setDefaultExpiration(redisDefaultExpiration);
    
        //                 
        Map cacheTimes = new HashMap<>();
        cacheTimes.put("people", new CacheTime(selectCacheTimeout, selectCacheRefresh));
        cacheTimes.put("people1", new CacheTime(120, 115));
        cacheTimes.put("people2", new CacheTime(120, 115));
        redisCacheManager.setCacheTimess(cacheTimes);
    
        return redisCacheManager;
    }
    

    残りの作成カットはメソッド情報をキャッシュします.前編を参照してください.
    ソースアドレス:https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases
    Spring-boot-student-cache-redis-2エンジニアリング
    監視のために生まれたマルチレベルキャッシュフレームlayering-cacheこれは私のオープンソースのマルチレベルキャッシュフレームワークの実現であり、興味があれば見てもいいです.