测试类:
@RestController @RequestMapping("/test") public class HelloController { @Autowired private AnaRedisUtils anaRedisUtils; @PostConstruct public void init() { MultilevelCacheUtils.createLoadingCache("test1", 10, 100, anaRedisUtils, 10, String.class, string -> { return string; }, string -> { // 从db获取结果 return null; } ); } @RequestMapping(value = "/test", method = RequestMethod.GET) public BaseResponse test() throws Exception { MultilevelCacheUtils.getResultWithCache("test1", "1"); return null; } }
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Maps; import com.pdd.service.ana.common.utils.AnaRedisUtils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Function; /** * @create 2020-08-04 * @description 多级缓存utils */ @Slf4j public class MultilevelCacheUtils { private static String NULL_CACHE = "null"; @Data @AllArgsConstructor static class CacheKeyMapValue { private LoadingCache<String, Optional<String>> loadingCache; private Function paramToCacheKeyFunc; private Function queryDataFunc; private AnaRedisUtils redisUtils; private int redisExpireSeconds; } private static Map<String, CacheKeyMapValue> CACHE_KEY_MAP = Maps.newConcurrentMap(); public static <T> void createLoadingCache(String cacheName, long expireAfterWriteDurationSeconds, long maximumSize, AnaRedisUtils redisUtils, int redisExpireSeconds, Class<T> dbQueryParamClass, Function<T, String> paramToCacheKeyFunc, Function<T, String> queryDataFunc) { if (CACHE_KEY_MAP.containsKey(cacheName)) { throw new IllegalArgumentException(cacheName + "缓存已存在"); } LoadingCache<String, Optional<String>> loadingCache = CacheBuilder.newBuilder() .expireAfterWrite(expireAfterWriteDurationSeconds, TimeUnit.SECONDS) .maximumSize(maximumSize) .build( new CacheLoader<String, Optional<String>>() { @Override public Optional<String> load(String cacheKey) { // 内存中查不到redis中获取 String result = null; try { result = redisUtils.get(cacheKey); } catch (Exception e) { log.error("getDataFromRedis失败,cacheKey=" + cacheKey, e); } // guavaCache不能缓存null,所以这里做了封装 return Optional.ofNullable(result); } }); CACHE_KEY_MAP.put(cacheName, new CacheKeyMapValue(loadingCache, paramToCacheKeyFunc, queryDataFunc, redisUtils, redisExpireSeconds)); } public static <T> String getResultWithCache(String cacheName, T dbQueryParam) { String result = null; CacheKeyMapValue cacheKeyMapValue = CACHE_KEY_MAP.get(cacheName); if (Objects.isNull(cacheKeyMapValue)) { throw new IllegalArgumentException(cacheName + "缓存规则不存在,需要先设置缓存规则"); } LoadingCache<String, Optional<String>> loadingCache = cacheKeyMapValue.getLoadingCache(); Function<T, String> paramToCacheKeyFunc = cacheKeyMapValue.getParamToCacheKeyFunc(); Function<T, String> queryDataFunc = cacheKeyMapValue.getQueryDataFunc(); AnaRedisUtils redisUtils = cacheKeyMapValue.getRedisUtils(); int redisExpireSeconds = cacheKeyMapValue.getRedisExpireSeconds(); String cacheKey = paramToCacheKeyFunc.apply(dbQueryParam); try { Optional<String> optionalResult = loadingCache.get(cacheKey); result = optionalResult.orElse(null); } catch (ExecutionException e) { log.error("cache执行失败,cacheKey=" + cacheKey, e); } if (Objects.isNull(result)) { // 获取结果 result = queryDataFunc.apply(dbQueryParam); // 缓存 try { if (Objects.isNull(result)) { result = NULL_CACHE; } redisUtils.setex(cacheKey, result, redisExpireSeconds); } catch (Exception e) { log.error("setDataToRedis失败,cacheKey=" + cacheKey + ";result=" + result + ";redisExpireSeconds=" + redisExpireSeconds, e); } } else if (NULL_CACHE.equals(result)) { result = null; } return result; } }