今天和大家聊聊阿里的一款缓存神器 JetCache。
谈及缓存,其实有许多方案可供选择。例如:Guava Cache、Caffine、Encache、Redis 等。
这些缓存技术都能满足我们的需求,但现在有一个问题是:技术是在不断发展的,若今后有更好的缓存技术出现,如果我们需要替换之前的缓存方案,该怎么办呢?
之前,我们已经了解到缓存方案有许多,而这些缓存在项目中的使用方式又不尽相同。这无疑将增加我们的学习成本以及开发成本(即:开发人员需要了解并掌握所使用缓存技术的使用方式以及原理)。
现如今我们的项目大多都是分布式环境,在分布式环境中有三大经典的分布式缓存问题:
针对这些问题,具体的缓存技术似乎并不能解决。那么,这就意味着需要开发人员每次在使用缓存时,都需要手动解决这些问题。针对这些固定的问题,是否可以采用成熟、统一的方案解决呢?
在开发过程中,我们经常会用到缓存来提高系统的性能和效率。然而,不同的缓存实现可能会有各自的接口和行为,这就导致了在切换缓存实现或者在不同的缓存实现之间共享数据时会遇到困难。
JSR-107,也被称为 JCache,是由 Java 定义的一项规范。JSR-107 规范定义了一套标准的 Java 缓存 API,使得开发者可以用一致的方式来使用和切换不同的缓存实现。这样,无论你使用哪种缓存技术,只要它们遵循 JSR-107 规范,你就可以用同样的方式来操作缓存。
JetCache 是阿里推出的一个基于 Java 的缓存系统封装,提供统一的 API 和注解来简化缓存的使用。 JetCache提供了比 SpringCache 更加强大的注解,可以原生的支持 TTL、两级缓存、分布式自动刷新,还提供了 Cache接口用于手工缓存操作。 当前有四个实现,RedisCache、TairCache(此部分未在 github 开源)、CaffeineCache(in memory) 和一个简易的 LinkedHashMapCache(in memory),要添加新的实现也是非常简单的。
| 支持项 | SpringCache | JetCache |
|---|---|---|
| JSR-107 | 支持 | 支持 |
| 本地缓存 | 支持 | 支持 |
| 远程缓存 | 支持 | 支持 |
| 注解缓存 | 支持 | 支持 |
| 对象缓存 | —— | 支持 |
| 分布式锁 | —— | 支持 |
| 缓存穿透 | 简单 | 灵活方案支持 |
| 缓存击穿 | 简单 | 灵活方案支持 |
| 缓存雪崩 | —— | 灵活方案支持 |
| 多级缓存 | 简单 | 灵活方案支持 |
| 扩展性 | 支持 | 支持 |
| 监控 | —— | 支持 |
| 高级API(异步,原始特性) | —— | 支持 |
上表是由阿里技术提供的与 SpringCache 功能性相关的对比。可以看出 JetCache 功能更加强大,并在许多方面都有着显著的优势。
我们基于 jetcache-starter-redis 包,以注解缓存为例快速带大家体验 JetCache 在处理缓存时有多方便。
<dependency>
<groupId>com.alicp.jetcachegroupId>
<artifactId>jetcache-starter-redisartifactId>
<version>2.7.5version>
dependency>
在 application.yml 配置文件中添加如下配置:
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: linkedhashmap
keyConvertor: fastjson
remote:
default:
type: redis
keyConvertor: fastjson2
broadcastChannel: projectA
valueEncoder: java
valueDecoder: java
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
host: 127.0.0.1
port: 6379
启用注解缓存
package com.company.mypackage;
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
// 激活 @Cached 注解
@EnableMethodCache(basePackages = "com.company.mypackage")
// 激活 @CreateCache 注解
@EnableCreateCacheAnnotation
public class MySpringBootApp {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApp.class);
}
}
使用注解缓存
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
@Cached(name = "cache:user:", key = "#id", expire = 3600, cacheType = CacheType.LOCAL)
public User getUserById(Integer id) {
return userMapper.selectUserById(id);
}
}
使用效果

针对缓存穿透,我们通常有两种解决方案:
JetCache 采用了第一种方式(即:缓存空对象)来解决缓存穿透问题。我们只需在注解中加上一个属性即可:
@Cached(cacheNullValue = true)
针对缓存击穿问题,JetCache 提供了两种方式解决:
JVM 内存锁级别的保护,旨在将并发重建的请求限制在可控范围。这种方式的核心思想是缓存重建任务可控。针对缓存雪崩问题,一般在缓存层通过两种方式解决:
JetCache 针对上面的两种解决方式也给出了对应的方案:
@CacheRefresh + CacheLoader 维护固定缓存多级缓存时 JetCache 一大特色,JetCache 多级缓存有以下特点:
Cache multiLevelCache = MultiLevelCacheBuilder
.createMultiLevelCacheBuilder()
.addCache(caffeineCache, memCache, redisCache)
.expireAfterWrite(100, TimeUnit.SECONDS)
.buildCache();
有时缓存也是需要管理的。例如:当更新数据库时,使缓存失效或者同步更新缓存。JetCache 可以通过以下两个注解实现缓存失效/更新:
之前,我们一直采用注解声明式的创建缓存。但是在某些情况下,我们需要使用编程式创建缓存的方式。JetCache 中可以使用 CacheManager 手动创建缓存。例如:
@Autowired
private CacheManager cacheManager;
private Cache<String, UserDO> userCache;
@PostConstruct
public void init() {
QuickConfig qc = QuickConfig.newBuilder("userCache")
.expire(Duration.ofSeconds(100))
.cacheType(CacheType.BOTH) // two level cache
.syncLocal(true) // invalidate local cache in all jvm process after update
.build();
userCache = cacheManager.getOrCreateCache(qc);
}
当配置参数 jetcache.statIntervalMinutes 大于 0 时,使用 @CreateCache 和 @Cached 生成的缓存将自带监控。JetCache 会按指定的时间定期通过 logger 输出统计信息。默认输出信息类似如下:

本文只简单的介绍了 JetCache 的相关概念,若有特殊开发需求的小伙伴可以自行前去官方文档探索哦。