多模块项目使用枚举配置spring-cache缓存

javalover123
• 阅读 118

一、前言

  1. 近期被刷接口了,考虑增加 本地缓存提高性能,另配置 限流
  2. 使用 spring-cache 注解式缓存,可以提高使用缓存的开发效率
  3. 不同业务,可以定制 自己的缓存策略,是基本需求
  4. 多模块项目,最好在 统一的模块(如 common) 加载缓存配置

二、方案

1. 配置缓存:接口 + 枚举 + Lombok

缓存配置接口:

public interface ICacheConfig {

    Integer getTtl();

}

common模块缓存配置(使用 Lombok 的 FieldNameConstants 自动生成 常量):

@lombok.Getter
@lombok.AllArgsConstructor
@lombok.experimental.FieldNameConstants(onlyExplicitlyIncluded = true)
public enum CommonCacheConfig implements ICacheConfig {

    @FieldNameConstants.Include QUOTE_LEVEL(1000, 2);

    private final Integer ttl;

}

业务模块缓存配置:

@lombok.Getter
@lombok.AllArgsConstructor
@lombok.experimental.FieldNameConstants(onlyExplicitlyIncluded = true)
public enum QuoteServiceCacheConfig implements ICacheConfig {

    @FieldNameConstants.Include HOT_STOCK(1000, 30);

    private final Integer ttl;

}

2. 多模块配置加载:Reflections + SimpleCacheManager

  • 通过 Reflections 库加载多模块配置

  • SimpleCacheManager 组合 各种不同配置的 缓存

    @EnableCaching
    @Configuration
    public class CacheConfig {
      private Logger log = LoggerFactory.getLogger(this.getClass());
      @Bean
      @Primary
      public CacheManager cacheManager() {
          final SimpleCacheManager cacheManager = new SimpleCacheManager();
    
          final String prefix = "package";
        Set<Class<? extends ICacheConfig>> classes = new Reflections(prefix).getSubTypesOf(ICacheConfig.class);
        log.info("cache types|{}|{}", prefix, classes);
        List<Cache> caches = classes.stream().flatMap(clazz -> Arrays.stream(clazz.getEnumConstants())).map(config -> {
            final Caffeine<Object, Object> cache = Caffeine.newBuilder().recordStats();
            Optional.ofNullable(config.getTtl()).ifPresent(t -> cache.expireAfterWrite(t, TimeUnit.SECONDS));
            return new CaffeineCache(((Enum) config).name(), cache.build());
        }).collect(Collectors.toList());
        cacheManager.setCaches(caches);
        return cacheManager;
    }

3. 使用缓存

  • 使用 @Cacheable(cacheNames = CommonCacheConfig.Fields.QUOTE_LEVEL, sync = true) 操作缓存

  • 使用 Lombok 的 FieldNameConstants 自动生成的 常量:

    public enum CommonCacheConfig implements ICacheConfig {
    
      public static final class Fields {
          public static final String QUOTE_LEVEL = "QUOTE_LEVEL";
      }
    

}


## 三、总结
- 通过 接口 + 枚举,业务模块不用改common模块, 新增枚举 就能 方便的配置、使用缓存,符合 开闭原则
- 通过 Lombok 的 FieldNameConstants 自动生成 枚举名称常量,便于代码 导航、重构
- 通过 Reflections 库,common模块自动加载 各模块的缓存配置,SimpleCacheManager 组合 各种不同配置的 缓存(CaffeineCacheManager 不能),降低使用成本,提高可维护性
- sync = true,加锁,只有一个线程去加载数据,其他线程阻塞,防止 缓存击穿
- [alibaba/jetcache](https://github.com/alibaba/jetcache/blob/master/introduce_CN.md):支持TTL和两级缓存、自动刷新和加载保护 等
- [netease-im/camellia](https://github.com/netease-im/camellia/blob/master/docs/cache/cache.md):网易开源,有意思的是  支持基于注解执行mget,mevict等批量操作

本文首先发布于 [https://www.890808.xyz/](https://www.890808.xyz/) ,其他平台需要审核更新慢一些。

![javalover123](https://img.890808.xyz/file/javalover123/2023/04/688b88cfd4ed9f6fcd56828b849ce47c.jpg)
点赞
收藏
评论区
推荐文章
happlyfox happlyfox
2年前
NetCore的缓存使用详例
关于我缓存基础知识缓存可以减少生成内容所需的工作,从而显著提高应用程序的性能和可伸缩性。缓存最适用于不经常更改的数据,生成成本很高。通过缓存,可以比从数据源返回的数据的副本速度快得多。应该对应用进行编写和测试,使其永不依赖于缓存的数据。ASP.NETCore支持多个不同的缓存。最简单的缓存基于IMemoryCac
京东云开发者 京东云开发者
6个月前
浅谈 HTTP 缓存与 CDN 缓存的那点事
HTTP缓存与CDN缓存一直是提升web性能的两大利器,合理的缓存配置可以降低带宽成本、减轻服务器压力、提升用户的体验。而不合理的缓存配置会导致资源界面无法及时更新,从而引发一系列的衍生问题。本文将分别将从HTTP缓存与cdn缓存的规则、流程、配置
京东云开发者 京东云开发者
4个月前
基于Spring Cache实现Caffeine、jimDB多级缓存实战
在早期参与涅槃氛围标签中台项目中,前台要求接口性能999要求50ms以下,通过设计Caffeine、ehcache堆外缓存、jimDB三级缓存,利用内存、堆外、jimDB缓存不同的特性提升接口性能,内存缓存采用Caffeine缓存,利用WTinyLFU算法获得更高的内存命中率;同时利用堆外缓存降低内存缓存大小,减少GC频率,同时也减少了网络IO带来的性能消耗;利用JimDB提升接口高可用、高并发;后期通过压测及性能调优999性能<20ms
混世魔王 混世魔王
3个月前
皕杰报表的缓存问题
设置缓存是提高报表性能的手段之一,皕杰报表在配置文件reportconfig.xml中对缓存进行设置。在系统模式中通过设置开发模式或生产模式来确定是否启用报表缓存。develop在缓存设置中设置缓存方式。1、使用皕杰内置的缓存(即:设置eh"false")
Stella981 Stella981
1年前
Guava的两种本地缓存策略
Guava的两种缓存策略缓存在很多场景下都需要使用,如果电商网站的商品类别的查询,订单查询,用户基本信息的查询等等,针对这种读多写少的业务,都可以考虑使用到缓存。在一般的缓存系统中,除了分布式缓存,还会有多级缓存,在提升一定性能的前提下,可以在一定程度上避免缓存击穿或缓存雪崩,也能降低分布式缓存的负载。Guav
Easter79 Easter79
1年前
SpringCache的事务管理与单元测试
项目背景在某个项目中,使用了SpringCacheredis作为缓存解决方案,jpa作为orm在单元测试时,在执行某步操作时,需要往缓存中放入数据,之后启用断言判断对应的缓存是否存在,结果全部报缓存不存在项目配置springCache@BeanpublicCacheManager
Stella981 Stella981
1年前
Hibernate(四)——缓存策略+lazy
Hibernate作为和数据库数据打交道的框架,自然会设计到操作数据的效率wenti,而对于一些频繁操作的数据,缓存策略就是提高其性能一种重要手段,而Hibernate框架是支持缓存的,而且支持一级和二级两种缓存,合理的使用缓存策略可以大大提高我们的操作数据效率,但是利用不能,可能会造成不必要的麻烦。一,一级缓存(Session缓
Stella981 Stella981
1年前
Mybatis(四)—— Mybatis 缓存
一、Mybatis缓存MyBatis包含一个非常强大的查询缓存特性,使用缓存可以使应用更快地获取数据,避免频繁的数据库交互二、Mybatis缓存分类1.一级缓存:SqlSession的缓存一级缓存默认会启用,想要关闭一级缓存可以在select标签上配置flushCache“true”;
Stella981 Stella981
1年前
Redis缓存的基本思想
1.缓存的基本思想很多朋友,只知道缓存可以提高系统性能以及减少请求相应时间,但是,不太清楚缓存的本质思想是什么。缓存的基本思想其实很简单,就是我们非常熟悉的空间换时间。不要把缓存想的太高大上,虽然,它的确对系统的性能提升的性价比非常高。其实,我们在学习使用缓存的时候,你会发现缓存的思想实际在操作系统或者其他地方都被大量用到。比如C
javalover123 javalover123
2星期前
怎么把Java枚举名称作为注解的属性值
Java注解的属性值,必须为常量。有些场景想把枚举名称设置为注解的属性值(如springcache用枚举配置缓存,使用时需要缓存名称)