当前位置:   article > 正文

SpringBoot两级缓存实现_caffine 和redis 双缓存机制 spring boot 代码

caffine 和redis 双缓存机制 spring boot 代码

 主要实现代码如下:

  1. /**
  2. * 1. 支持解析 @CacheConfig.cacheNames ,独立为每个方法设置过期时间,单位秒
  3. * 2. 返回增强后的Cache实例 :new RedisPlusCache
  4. *
  5. * @see RedisPlusCache
  6. **/
  7. public class RedisCachePlusManager extends RedisCacheManager {
  8. private final CachePlusConfig config;
  9. public RedisCachePlusManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, CachePlusConfig config) {
  10. super(cacheWriter, defaultCacheConfiguration);
  11. this.config = config.clone();
  12. }
  13. @NonNull
  14. @Override
  15. protected RedisCache createRedisCache(@NonNull String name, @Nullable RedisCacheConfiguration cacheConfig) {
  16. String[] array = name.split(this.config.getDelimiter());
  17. name = array[0];
  18. if (array.length > 1 && cacheConfig != null) {
  19. long ttl = Long.parseLong(array[1]);
  20. cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
  21. }
  22. if (array.length > 2) {
  23. this.config.setDuration(Long.parseLong(array[2]));
  24. }
  25. RedisCache redisCache = super.createRedisCache(name, cacheConfig);
  26. return new RedisPlusCache(redisCache.getName(), redisCache.getNativeCache(), redisCache.getCacheConfiguration(), this.config);
  27. }
  28. }
  1. /**
  2. * 1. 基于CaffeineCache实现二级缓存
  3. *
  4. * @see CachePlusConfig
  5. **/
  6. public class RedisPlusCache extends RedisCache {
  7. private final Map<Long, Cache<Object, Object>> caffeineCaches;
  8. private final CachePlusConfig config;
  9. protected RedisPlusCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig, CachePlusConfig config) {
  10. super(name, cacheWriter, cacheConfig);
  11. this.caffeineCaches = new ConcurrentHashMap<>();
  12. this.config = config;
  13. }
  14. @Override
  15. protected Object lookup(@NonNull Object key) {
  16. RedisCacheConfiguration cacheConfiguration = super.getCacheConfiguration();
  17. Duration duration = cacheConfiguration.getTtl();
  18. Cache<Object, Object> localCache = this.caffeineCaches.get(duration.getSeconds());
  19. if (localCache == null) return this.lookupAndPutLocal(key);
  20. Object v = localCache.get(key, oneKey -> null);
  21. return v == null ? this.lookupAndPutLocal(key) : v;
  22. }
  23. @Override
  24. public void put(@NonNull Object key, @Nullable Object value) {
  25. this.putLocal(key, value);
  26. super.put(key, value);
  27. }
  28. private Object lookupAndPutLocal(@NonNull Object key) {
  29. Object v = super.lookup(key);
  30. this.putLocal(key, v);
  31. return v;
  32. }
  33. private void putLocal(Object key, Object value) {
  34. Object cacheValue = preProcessCacheValue(value);
  35. if (cacheValue != null) {
  36. Cache<Object, Object> localCache = this.createLocalCache();
  37. localCache.put(key, cacheValue);
  38. }
  39. }
  40. private Cache<Object, Object> createLocalCache() {
  41. long time = this.getCacheConfiguration().getTtl().getSeconds();
  42. Cache<Object, Object> localCache = this.caffeineCaches.get(time);
  43. if (localCache != null) return localCache;
  44. synchronized (this) {
  45. localCache = this.caffeineCaches.get(time);
  46. if (localCache != null) return localCache;
  47. localCache = Caffeine.newBuilder()
  48. .initialCapacity((int) (this.config.getDuration() >> 2))
  49. .expireAfterWrite(this.config.getDuration(), TimeUnit.SECONDS)
  50. .maximumSize(this.config.getMaximumSize()).build();
  51. this.caffeineCaches.put(time, localCache);
  52. return localCache;
  53. }
  54. }
  55. }
  1. /**
  2. * 缓存配置类
  3. **/
  4. @Data
  5. public class CachePlusConfig implements Serializable, Cloneable {
  6. // 分隔符
  7. private String delimiter;
  8. // 过期时间
  9. private long duration;
  10. // 最大缓存容量
  11. private long maximumSize;
  12. public CachePlusConfig() {
  13. this.delimiter = "##";
  14. this.duration = 600L;
  15. this.maximumSize = 100_000L;
  16. }
  17. @Override
  18. public CachePlusConfig clone() {
  19. try {
  20. return (CachePlusConfig) super.clone();
  21. } catch (CloneNotSupportedException e) {
  22. throw new IllegalStateException("clone error", e);
  23. }
  24. }
  25. }

注册类:

  1. @Configuration
  2. @EnableCaching
  3. public class SpringCacheConfig {
  4. @Resource
  5. @Qualifier("redisConnectionFactory")
  6. private RedisConnectionFactory redisConnectionFactory;
  7. @Resource
  8. private CachePlusConfig localCachePlusConfig;
  9. @ConfigurationProperties("cache-plus.local")
  10. @Bean
  11. public CachePlusConfig get() {
  12. return new CachePlusConfig();
  13. }
  14. @Bean("commonKeyGenerator")
  15. public KeyGenerator getCommonKeyGenerator() {
  16. return new CommonKeyGenerator();
  17. }
  18. @Primary
  19. @Bean
  20. public CacheManager cacheManager() {
  21. RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
  22. .entryTtl(Duration.ofSeconds(300));
  23. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  24. GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
  25. redisCacheConfiguration = redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
  26. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericFastJsonRedisSerializer));
  27. return new RedisCachePlusManager(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory), redisCacheConfiguration, localCachePlusConfig);
  28. }
  29. @Bean("localCacheManager")
  30. public CacheManager localCacheManager() {
  31. CaffeineCacheManager localCacheManager = new CaffeineCachePlusManager(this.localCachePlusConfig);
  32. localCacheManager.setAllowNullValues(true);
  33. return localCacheManager;
  34. }
  35. }
  1. @Configuration
  2. public class RedisCommonConfig {
  3. @ConditionalOnMissingBean(name = "redisTemplate")
  4. @Bean(name = "redisTemplate")
  5. public RedisTemplate<String, Object> redisTemplate(@Qualifier("redisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
  6. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
  7. redisTemplate.setConnectionFactory(redisConnectionFactory);
  8. GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
  9. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  10. // 设置value的序列化规则和 key的序列化规则
  11. redisTemplate.setKeySerializer(stringRedisSerializer);
  12. redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
  13. redisTemplate.setHashKeySerializer(stringRedisSerializer);
  14. redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
  15. redisTemplate.setDefaultSerializer(genericFastJsonRedisSerializer);
  16. redisTemplate.setEnableDefaultSerializer(true);
  17. return redisTemplate;
  18. }
  19. @ConditionalOnMissingBean(name = "redisConnectionFactory")
  20. @Bean(name = "redisConnectionFactory")
  21. public RedisConnectionFactory redisConnectionFactory() {
  22. RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration("127.0.0.1", 6379);
  23. return new LettuceConnectionFactory(configuration);
  24. }
  25. }

扩展,单独注册只支持二级缓存(本地缓存)的CacheManager,基于CaffeineCache实现:

  1. /**
  2. * 1. 支持解析 @CacheConfig.cacheNames ,独立为每个方法设置过期时间,单位秒
  3. **/
  4. @Slf4j
  5. public class CaffeineCachePlusManager extends CaffeineCacheManager {
  6. private final CachePlusConfig config;
  7. public CaffeineCachePlusManager(CachePlusConfig config) {
  8. this.config = config;
  9. }
  10. @NonNull
  11. @Override
  12. protected com.github.benmanes.caffeine.cache.Cache<Object, Object> createNativeCaffeineCache(@NonNull String name) {
  13. long expire = this.getExpire(name);
  14. if (expire > 0) {
  15. return Caffeine.newBuilder()
  16. .initialCapacity((int)(this.config.getMaximumSize() >> 4))
  17. // 3600秒后自动删除
  18. .expireAfterWrite(expire, TimeUnit.SECONDS)
  19. // 最大容量10w个,超过会自动清理空间
  20. .maximumSize(this.config.getMaximumSize())
  21. .removalListener(((key, value, cause) -> {
  22. //清理通知 key, value ==> 键值对 cause ==> 清理原因
  23. log.info("caffeine cache removed: {}, {}, {}", key, value, cause);
  24. })).build();
  25. }
  26. return super.createNativeCaffeineCache(name);
  27. }
  28. private long getExpire(@NonNull String name) {
  29. if (this.config.getDelimiter() == null) return this.config.getDuration();
  30. String[] split = name.split(this.config.getDelimiter());
  31. return split.length > 1 ? Long.parseLong(split[1]) : this.config.getDuration();
  32. }
  33. }

扩展,键生成器

  1. /**
  2. * 通用的缓存键生成器
  3. **/
  4. public class CommonKeyGenerator implements KeyGenerator {
  5. @NonNull
  6. @Override
  7. public Object generate(Object target, Method method, @NonNull Object... params) {
  8. return target.getClass().getName() + ":" + method.getName() + ":" + generateKey(params);
  9. }
  10. /**
  11. * Generate a key based on the specified parameters.
  12. */
  13. public static Object generateKey(Object... params) {
  14. if (params.length == 0) {
  15. return SimpleKey.EMPTY;
  16. }
  17. if (params.length == 1) {
  18. Object param = params[0];
  19. if (param != null && !param.getClass().isArray()) {
  20. return param;
  21. }
  22. }
  23. return StringUtils.arrayToDelimitedString(params, "-");
  24. }
  25. }
  1. /**
  2. * 缓存键生成器-从方法开始拼接
  3. **/
  4. public class MethodKeyGenerator implements KeyGenerator {
  5. @NonNull
  6. @Override
  7. public Object generate(@NonNull Object target, Method method, @NonNull Object... params) {
  8. return method.getName() + ":" + CommonKeyGenerator.generateKey(params);
  9. }
  10. }

业务代码示例:

  1. @CacheConfig(cacheNames = "DICT##120", keyGenerator = "commonKeyGenerator")
  2. @Service
  3. public class RegionService {
  4. // 过期时间、键生成,继承@CacheConfig
  5. @Cacheable
  6. public String getCityName(String code) {
  7. if ("010".equals(code)) return "beijing";
  8. else if (("020".equals(code))) return "shanghai";
  9. else return "shenzhen";
  10. }
  11. @Cacheable
  12. public String getCityName(String code, String lang) {
  13. if ("010".equals(code)) return "beijing";
  14. else if (("020".equals(code))) return "shanghai";
  15. else return "shenzhen";
  16. }
  17. // 该方法独立设置过期时间240秒
  18. @Cacheable(cacheNames = "DICT##240")
  19. public String getCityName240(String code, String lang) {
  20. if ("010".equals(code)) return "beijing";
  21. else if (("020".equals(code))) return "shanghai";
  22. else return "shenzhen";
  23. }
  24. // 该方法只支持二级缓存
  25. @Cacheable(cacheNames = "DICT##10", cacheManager = "localCacheManager")
  26. public String getCityNameByLocal(String code) {
  27. if ("010".equals(code)) return "beijing";
  28. else if (("020".equals(code))) return "shanghai";
  29. else return "shenzhen";
  30. }
  31. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/929810
推荐阅读
相关标签
  

闽ICP备14008679号