Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

SpringBoot 集成 Caffeine Cache

SpringBoot 集成 Caffeine Cache 实践。

简介

Caffeine 是一个高性能的Java本地缓存库,采用 W-TinyLFU 算法(结合 LRU 和 LFU 优势),显著提升缓存命中率,尤其在复杂访问模式下表现优异。

接入

添加依赖

Java 8

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.3</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

Java 11 及以上

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

配置 Caffeine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.cache.annotation.EnableCaching;

import java.util.concurrent.TimeUnit;

@EnableCaching
@Configuration
public class LocalCacheConfiguration {

@Bean
@Primary
public CacheManager defaultCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS)
.maximumSize(1024));
return cacheManager;
}

@Bean
public CacheManager ephemeralCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(5, java.util.concurrent.TimeUnit.SECONDS)
.maximumSize(1024));
return cacheManager;
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Cacheable(value = "msg:sub", key = "#p0 + #p1 + #p2.getType()",
cacheManager = "ephemeralCacheManager")
public boolean checkUserSubscribe(Long tenantId, String productKey,
SubscribeMsgTypeEnum msgTypeEnum) {
if (loggerConfig.isLogEnable(OPEN_DEVICE_MSG_LOG)) {
log.info("checkUserSubscribe cache not matched, tenantId:{}, productKey:{}, type:{}",
tenantId, productKey, msgTypeEnum.getType());
}
List<String> subscribedTypeList = tenantCustomConfigComponent.getCachedSubscribeMsgTypeList(tenantId, productKey);
if (subscribedTypeList == null) {
return false;
}
return subscribedTypeList.contains(msgTypeEnum.getType());
}

@Cacheable(value = "prd:tnt", key = "#p0",
cacheManager = "defaultCacheManager", unless = "#result == null")
public Long getProductTenantId(String productKey) {
if (loggerConfig.isLogEnable(OPEN_DEVICE_MSG_LOG)) {
log.info("getProductTenantId cache not matched, productKey:{}", productKey);
}
// 找到产品对应的租户 owner_id
Long tenantId = productComponent.getCachedProductTenantId(productKey);
return tenantId;
}

注意点

  • @Cacheable 注解是依赖于 AOP 实现的, 所以必须跨类使用,同时方法必须是 public
  • @Cacheable 中的 key 是SpEL 表达式,注意语法,如果拿不到参数名,可以使用 p0 按序号获取参数标识
  • @Cacheable 支持特定情况下缓存或不缓存,参见 unlesscondition 属性
  • 不要忘记配置 @EnableCaching 启用缓存支持
  • Java 8 支持的版本是 2.X
  • 可以使用 Cache cache = cacheManager.getCache("prd:tnt"); 手动操作缓存