caffeine+redis做二级缓存

  1. caffeine+redis做二级缓存

    caffeine是基于Java8的本地缓存(进程内缓存)

    • pom.xml注入依赖

      <!-- 注入redis依赖 -->
      		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
      <dependency>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      
      <dependency>
      	<groupId>com.github.ben-manes.caffeine</groupId>
      	<artifactId>caffeine</artifactId>
      </dependency>
      
    • application.properties配置文件

      #用来控制redis是否生效 1生效
      spring.redis1.enabled=1
      
    • CacheConfig 配置caffeine缓存的基本信息 名称、超时时长、最大容量等

      import java.util.ArrayList;
      import java.util.concurrent.TimeUnit;
      
      import org.springframework.cache.CacheManager;
      import org.springframework.cache.annotation.EnableCaching;
      import org.springframework.cache.caffeine.CaffeineCache;
      import org.springframework.cache.support.SimpleCacheManager;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.context.annotation.Profile;
      
      import com.github.benmanes.caffeine.cache.Caffeine;
      
      //@Profile("cacheenable")   //指定当前环境 不指定,任何环境下都能注册这个组件
      @Configuration
      @EnableCaching //开启缓存
      public class CacheConfig {
      	public static final int DEFAULT_MAXSIZE = 288000;
      	public static final int DEFAULT_TTL = 1000;
      
      	private SimpleCacheManager cacheManager = new SimpleCacheManager();
      	
      	// 定义cache名称、超时时长(秒)、最大容量
      	public enum CacheEnum {
      		homePage(288000, 1000), // 有效期8个小时 , 最大容量1000
      		;
      		CacheEnum(int ttl, int maxSize) {
      			this.ttl = ttl;
      			this.maxSize = maxSize;
      		}
      
      		private int maxSize = DEFAULT_MAXSIZE; // 最大數量
      		private int ttl = DEFAULT_TTL; // 过期时间(秒)
      
      		public int getMaxSize() {
      			return maxSize;
      		}
      
      		public int getTtl() {
      			return ttl;
      		}
      	}
      
      	// 创建基于Caffeine的Cache Manager
      	@Bean
      	@Primary
      	public CacheManager caffeineCacheManager() {
      		ArrayList<CaffeineCache> caches = new ArrayList<CaffeineCache>();
      		// 最后一次写入或访问后经过8H过期
      		for (CacheEnum c : CacheEnum.values()) {
      			caches.add(new CaffeineCache(c.name(), Caffeine.newBuilder().recordStats()
      					.expireAfterAccess(c.getTtl(), TimeUnit.SECONDS).maximumSize(c.getMaxSize()).build()));
      		}
      		cacheManager.setCaches(caches);
      		return cacheManager;
      	}
      
      	@Bean
      	public CacheManager getCacheManager() {
      		return cacheManager;
      	}
      }
      
    • 代码

      /**
       * value 对应的是CacheConfig中的cache名称
       * sync 是否异步
       * 
       */
      @Cacheable(value = "homePage", key = "'live'.concat(':').concat(#source).concat(':').concat(#channelId)", sync = true)
      	public String makeLiveUrl(Long channelId, Integer source) {
      		
      		// 作成url
      		String prefixUrl = "";
      		if (redis1enabled == 1) {
      			logger.info("从redis中获取数据");
      			Object redisResultUrl = redisUtil.get("live:" + source + ":" + String.valueOf(channelId));
      			if (redisResultUrl == null) {
      				logger.info("从数据库中获取数据");
                      // getUrlForDb 取数据库中的数据
      				prefixUrl = getUrlForDb(channelId, source, UrlTypeEnum.LIVE_TYPE.getValue());
      
      				if (prefixUrl == null) {
      					redisUtil.set("live:" + source + ":" + String.valueOf(channelId), "-1", 0);
      				} else {
      					redisUtil.set("live:" + source + ":" + String.valueOf(channelId), prefixUrl, 0);
      				}
      			} else {
      				if (redisResultUrl.equals("-1")) {
      					prefixUrl = null;
      				} else {
      					prefixUrl = (String) redisResultUrl;
      				}
      			}
      		} else {
      			prefixUrl = getUrlForDb(channelId, source, UrlTypeEnum.LIVE_TYPE.getValue());
      		}
      
      		return prefixUrl;
      	}
      

    因为 Spring Cache是基于切面的(基于AOP的动态代理实现的:即都在方法调用前后去获取方法的名称、参数、返回值,然后根据方法名称、参数生产缓存的key,进行缓存),所以内部方法调用不会调用切面,导致缓存不生效,可以写一个工具类,使用内部调用的时候,自己实例化一个对象,让类走AOP


  2. Caffeine 获取本地所有的key

    List<String> list = new ArrayList<>();
    // 获取caffeine对应的缓存
    Cache cache = CacheConfig.cacheManager.getCache("channel");
    Object nativeCache = cache.getNativeCache();
    try {
    	@SuppressWarnings("unchecked")
    	// map存放的是key-value
    	Map<String, Object> map = (Map<String, Object>) ObjToMap(nativeCache).get("cache");
    	for (Map.Entry<String, Object> entry : map.entrySet()) {
    		// 循环获取key
    		list.add(entry.getKey());
    	}
    } catch (Exception e) {
    	logger.warn("DictionaryItem缓存错误!!!");
    }
    		
    // caffeine缓存所有的key
    System.out.println(list);
    

    因为cache.getNativeCache();的返回值是object型,所有需要object转map

    	/**
    	 * Obj 转换为 map
    	 * 
    	 * @param obj
    	 * @return
    	 * @throws Exception
    	 */
    	public static Map<String, Object> ObjToMap(Object obj) throws Exception {
    		Map<String, Object> map = new HashMap<>();
    		Field[] fields = obj.getClass().getDeclaredFields();
    		for (Field field : fields) {
    			field.setAccessible(true);
    			map.put(field.getName(), field.get(obj));
    		}
    		return map;
    	}
    
posted @ 2021-06-28 15:18  FY丶  阅读(856)  评论(0)    收藏  举报