­

手把手教你使用 Spring Boot 3 開發上線一個前後端分離的生產級系統(六) – 本地緩存 Caffeine 和 分佈式緩存 Redis 集成與配置

本地緩存 Caffeine 集成和配置

Caffeine 是 Java 8 對 Google Guava 緩存的重寫,是一個提供了近乎最佳命中率的高性能的緩存庫。我們按照如下步驟集成和配置:

  1. 添加 spring-boot-starter-cache 依賴

使用 spring-boot-starter-cache 「Starter」 可以快速添加基本緩存依賴項。 starter 引入了 spring-context-support。如果我們手動添加依賴項,則必須包含 spring-context-support 才能使用 JCache 或 Caffeine 支持。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  1. 添加 caffeine 依賴
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>
  1. 自定義緩存管理器
/**
    * Caffeine 緩存管理器
    */
@Bean
public CacheManager caffeineCacheManager() {
    SimpleCacheManager cacheManager = new SimpleCacheManager();

    List<CaffeineCache> caches = new ArrayList<>(CacheConsts.CacheEnum.values().length);
    for (CacheConsts.CacheEnum c : CacheConsts.CacheEnum.values()) {
        if (c.isLocal()) {
            Caffeine<Object, Object> caffeine = Caffeine.newBuilder().recordStats().maximumSize(c.getMaxSize());
            if (c.getTtl() > 0) {
                caffeine.expireAfterWrite(Duration.ofSeconds(c.getTtl()));
            }
            caches.add(new CaffeineCache(c.getName(), caffeine.build()));
        }
    }

    cacheManager.setCaches(caches);
    return cacheManager;
}
  1. 使用 @EnableCaching 註解開啟緩存
@SpringBootApplication
@MapperScan("io.github.xxyopen.novel.dao.mapper")
@EnableCaching
@Slf4j
public class NovelApplication {

	public static void main(String[] args) {
		SpringApplication.run(NovelApplication.class, args);
	}

	@Bean
	public CommandLineRunner commandLineRunner(ApplicationContext context){
		return args -> {
			Map<String, CacheManager> beans = context.getBeansOfType(CacheManager.class);
			log.info("加載了如下緩存管理器:");
			beans.forEach((k,v)->{
				log.info("{}:{}",k,v.getClass().getName());
				log.info("緩存:{}",v.getCacheNames());
			});

		};
	}

}

這樣我們就可以使用 Spring Cache 的註解(例如 @Cacheable)開發了。

分佈式緩存 Redis 集成和配置

本地緩存雖然有着訪問速度快的優點,但無法進行大數據的存儲。並且當我們集群部署多個服務節點,或者後期隨着業務發展進行服務拆分後,沒法共享緩存和保證緩存數據的一致性。
本地緩存的數據還會隨應用程序的重啟而丟失,這樣對於需要持久化的數據滿足不了需求,還會導致重啟後數據庫瞬時壓力過大。

所以本地緩存一般適合於緩存只讀數據,如統計類數據,或者每個部署節點獨立的數據。其它情況就需要用到分佈式緩存了。

分佈式緩存的集成步驟和本地緩存基本差不多,除了替換 caffeine 的依賴項為我們 redis 的依賴和配置上我們自定義的 redis 緩存管理器外,還要在配置文件中加入 redis 的連接配置:

  1. 加入依賴
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置 redis 緩存管理器
/**
 * 緩存配置類
 *
 * @author xiongxiaoyang
 * @date 2022/5/12
 */
@Configuration
public class CacheConfig {

    /**
     * Caffeine 緩存管理器
     */
    @Bean
    @Primary
    public CacheManager caffeineCacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();

        List<CaffeineCache> caches = new ArrayList<>(CacheConsts.CacheEnum.values().length);
        for (CacheConsts.CacheEnum c : CacheConsts.CacheEnum.values()) {
            if (c.isLocal()) {
                Caffeine<Object, Object> caffeine = Caffeine.newBuilder().recordStats().maximumSize(c.getMaxSize());
                if (c.getTtl() > 0) {
                    caffeine.expireAfterWrite(Duration.ofSeconds(c.getTtl()));
                }
                caches.add(new CaffeineCache(c.getName(), caffeine.build()));
            }
        }

        cacheManager.setCaches(caches);
        return cacheManager;
    }

    /**
     * Redis 緩存管理器
     */
    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);

        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .disableCachingNullValues().prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX);

        Map<String, RedisCacheConfiguration> cacheMap = new LinkedHashMap<>(CacheConsts.CacheEnum.values().length);
        for (CacheConsts.CacheEnum c : CacheConsts.CacheEnum.values()) {
            if (c.isRemote()) {
                if (c.getTtl() > 0) {
                    cacheMap.put(c.getName(), RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
                            .prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX).entryTtl(Duration.ofSeconds(c.getTtl())));
                } else {
                    cacheMap.put(c.getName(), RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
                            .prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX));
                }
            }
        }

        RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig, cacheMap);
        redisCacheManager.setTransactionAware(true);
        redisCacheManager.initializeCaches();
        return redisCacheManager;
    }

}
  1. application.yml 中加入 redis 連接配置信息
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456