首頁  >  文章  >  資料庫  >  怎麼使用caffeine_redis自訂二級緩存

怎麼使用caffeine_redis自訂二級緩存

WBOY
WBOY轉載
2023-05-27 10:08:241199瀏覽

    問題

    基於提出的需求,我認為主要有以下兩個問題:

    • 因為有本地緩存,如何保證資料一致性。當一個節點資料改變,其他節點的資料如何失效?

    • 資料不對,需要重新同步,快取如何失效?

    流程圖

    接下來就是配合產品和其他開發人員畫出流程圖,如下:

    • 使用一張配置表,記錄是否需要緩存,是否開啟緩存,來達到通知時候緩存失效的狀況。

    • 因為專案要求一般,即使訊息遺失,也不會有太大的影響,所以最後選擇了 redis 裡面的訂閱、發布功能,實作通知其他節點失效本地快取。

    開發

    上面問題清楚了,流程圖也清楚了。那就準備開始寫 bug 了。整體思路是自訂註解實現切面,盡量降低對業務程式碼的耦合度。

    CacheConfig

    主要是在程式碼中解釋,定義一個 CacheManager 以便與業務結合。需要特別注意最大可緩存條數,以免佔用程式記憶體過多導致記憶體爆滿。當然也不能太小了,因為還要考慮命中率的問題。所以這就得結合實際得業務來決定最終的大小。

    @Bean(name = JKDDCX)
    @Primary
    public CacheManager cacheManager() {
         CaffeineCacheManager cacheManager  = new CaffeineCacheManager();
            cacheManager.setCaffeine(Caffeine.newBuilder()
                    // 设置最后一次写入或访问后经过固定时间过期
                    .expireAfterAccess(EXPIRE, TIME_UNIT)
                    //设置本地缓存写入后过期时间
                    .expireAfterWrite(EXPIRE, TIME_UNIT)
                    // 初始的缓存空间大小
                    .initialCapacity(500)
                    // 缓存的最大条数
                    .maximumSize(1000));// 使用人数 * 5 (每个人不同的入参 5 条)\
      return cacheManager;
    }

    @CaffeineCache

    自訂註解,把可以用到的參數都能加上。

    @Target({ ElementType.METHOD ,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CaffeineCache {
    
         public String moudleId() default "";
        
         //用于在数据库中配置参数
         public String methodId() default "";
         public String cachaName() default "";
        
         //动态切换实际的 CacheManager
         public String cacheManager() default "";
    
    }

    CacheMessageListener

    快取監聽器,主要是確保多節點資料一致性的問題。當一個節點快取更新,通知其他的節點相應處理。使用 Redis 的發布訂閱功能,並實作MessageListener 介面來實作主要技術。

    當然下面還有個細節就是一般生產環境是禁用 Redis#keys 指令的,所以得換個方式掃描對應的 key。

    public class CacheMessageListener implements MessageListener {
         @Override
        public void onMessage(Message message, byte[] pattern) {
            CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());
            logger.info("收到redis清除缓存消息, 开始清除本地缓存, the cacheName is {}, the key is {}", cacheMessage.getCacheName(), cacheMessage.getKey());
    //		redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey());
    
            /**
             * 如果是一个类上使用了 注解 @CaffeineCache ,那么所有接口都会缓存。
             * 下面的逻辑是:除了当前模块的接口访问的入参 key,其他的 redis 缓存都会被清除
             * (比如此模块的表更新了,但是当前调用此接口只是缓存了当前这个入参的redis,其他的数据删除)
             */
            String prefixKey = RedisConstant.WXYMG_DATA_CACHE + cacheMessage.getCacheName();
            Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
                Set<String> keysTmp = new HashSet<>();
                Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().
                        match(prefixKey + "*").
                        count(50).build());
                while (cursor.hasNext()) {
                    keysTmp.add(new String(cursor.next()));
                }
                return keysTmp;
            });
            Iterator iterator = keys.iterator();
            while (iterator.hasNext()) {
                if (iterator.next().toString().equals(cacheMessage.getKey())) {
                    iterator.remove();
                }
            }
            redisTemplate.delete(keys);
    
            cacheConfig.cacheManager().getCache(cacheMessage.getCacheName()).clear(); //cacheName 下的都删除
        }
    }

    CaffeineCacheAspect

    然後就是切面的邏輯處理,裡面的內容和 流程圖 一模一樣,只是使用程式碼實現了需求。

    其中:下面的程式碼是 Redis 發布訊息。

    redisTemplate.convertAndSend(CacheConfig.TOPIC, new CacheMessage(caffeineCache.cachaName(), redisKey));

    CacheMessage

    這是在 Redis 發布訊息的時候一個訊息體,也是自訂的,可以加更多的參數屬性

    public class CacheMessage implements Serializable {
    
    	private static final long serialVersionUID = -1L;
    
    	private String cacheName;
    
    	private Object key;
    
    	public CacheMessage(String cacheName, Object key) {
    		super();
    		this.cacheName = cacheName;
    		this.key = key;
    	}
    
    }

    以上是怎麼使用caffeine_redis自訂二級緩存的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除