阿里這款多級緩存框架一定要掌握,非常不錯!
共 16149字,需瀏覽 33分鐘
·
2024-07-23 19:00
來源:juejin.cn/user/4068629263428382/posts
-
1. jetcache簡介 -
2. jetcache使用 -
3. 測試 -
4. 常見報錯
在實(shí)際應(yīng)用中,并不是單一的使用本地緩存或者redis,更多是組合使用來滿足不同的業(yè)務(wù)場景,于是如何優(yōu)雅的組合本地緩存和遠(yuǎn)程緩存就成了我們要研究的問題,而這一點(diǎn),阿里開源的jetcache組件幫我們實(shí)現(xiàn)了
1. jetcache簡介
jetcache是阿里開源的基于java開發(fā)的緩存框架,支持多種緩存類型:本地緩存、分布式緩存、多級緩存。能夠滿足不同業(yè)務(wù)場景的緩存需求。
jetcache具有上手簡單、性能高效、拓展性強(qiáng)的特點(diǎn)。支持緩存預(yù)熱 、緩存key前綴等功能。結(jié)合spring-cache使用,可以實(shí)現(xiàn)十分優(yōu)雅的緩存類型切換
官網(wǎng)地址:https://github.com/alibaba/jetcache
官方文檔:https://github.com/alibaba/jetcache/tree/master/docs/CN
2. jetcache使用
1、引入依賴,這里我們使用sringboot項目框架,同時使用redis作為遠(yuǎn)程緩存。于是我們引入jetcache-starter-redis依賴,這里我的springboot版本為2.6.13
如果是非springboot項目可以參考官網(wǎng)說明配置
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.7.0</version>
</dependency>
<!-- jetcache2.7.x版本需要額外添加該依賴-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
對應(yīng)的版本說明如下:https://github.com/alibaba/jetcache/blob/master/docs/CN/Compatibility.md
2、修改配置文件,配置redis地址和線程數(shù)
jetcache:
# 統(tǒng)計間隔,0表示不統(tǒng)計,開啟后定期在控制臺輸出緩存信息
statIntervalMinutes: 15
# 是否把cacheName作為遠(yuǎn)程緩存key前綴
areaInCacheName: false
# 本地緩存配置
local:
default: # default表示全部生效,也可以指定某個cacheName
# 本地緩存類型,其他可選:caffeine/linkedhashmap
type: linkedhashmap
keyConvertor: fastjson
# 遠(yuǎn)程緩存配置
remote:
default: # default表示全部生效,也可以指定某個cacheName
type: redis
# key轉(zhuǎn)換器方式n
keyConvertor: fastjson
broadcastChannel: projectA
# redis序列化方式
valueEncoder: java
valueDecoder: java
# redis線程池
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
# redis地址與端口
host: 127.0.0.1
port: 6379
更詳細(xì)的參數(shù)配置可參考官網(wǎng)說明:
3、啟動類添加注解@EnableCreateCacheAnnotation,開啟緩存,添加@EnableMethodCache(basePackages = "com.example.jetcachedemo")注解,配置緩存方法掃描路徑
4、使用緩存可以通過三種方式:
-
方式一(推薦)AOP模式:通過 @Cached,@CacheUpdate,@CacheInvalidate注解
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping("getRemote")
@Cached(name="userCache:", key = "#id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.REMOTE)
public User getRemote(Long id){
// 直接新建用戶,模擬從數(shù)據(jù)庫獲取數(shù)據(jù)
User user = new User();
user.setId(id);
user.setName("用戶remote"+id);
user.setAge(23);
user.setSex(1);
System.out.println("第一次獲取數(shù)據(jù),未走緩存:"+id);
return user;
}
@GetMapping("getLocal")
@Cached(name="userCache:", key = "#id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL)
public User getLocal(Long id){
// 直接新建用戶,模擬從數(shù)據(jù)庫獲取數(shù)據(jù)
User user = new User();
user.setId(id);
user.setName("用戶local"+id);
user.setAge(23);
user.setSex(1);
System.out.println("第一次獲取數(shù)據(jù),未走緩存:"+id);
return user;
}
@GetMapping("getBoth")
@Cached(name="userCache:", key = "#id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.BOTH)
public User getBoth(Long id){
// 直接新建用戶,模擬從數(shù)據(jù)庫獲取數(shù)據(jù)
User user = new User();
user.setId(id);
user.setName("用戶both"+id);
user.setAge(23);
user.setSex(1);
System.out.println("第一次獲取數(shù)據(jù),未走緩存:"+id);
return user;
}
@PostMapping("updateUser")
@CacheUpdate(name = "userCache:", key = "#user.id", value = "#user")
public Boolean updateUser(@RequestBody User user){
// TODO 更新數(shù)據(jù)庫
return true;
}
@PostMapping("deleteUser")
@CacheInvalidate(name = "userCache:", key = "#id")
public Boolean deleteUser(Long id){
// TODO 從數(shù)據(jù)庫刪除
return true;
}
}
這里要注意實(shí)體類User一定要實(shí)現(xiàn)序列化,即聲明Serializable
@Data
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
private Integer sex;
}
-
方式二 API模式:通過 @CreateCache,注:在jetcache 2.7 版本CreateCache注解已廢棄,不推薦使用
@RestController
@RequestMapping("user2")
public class User2Controller {
@CreateCache(name= "userCache:", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.BOTH)
private Cache<Long, Object> userCache;
@GetMapping("get")
public User get(Long id){
if(userCache.get(id) != null){
return (User) userCache.get(id);
}
User user = new User();
user.setId(id);
user.setName("用戶both"+id);
user.setAge(23);
user.setSex(1);
userCache.put(id, user);
System.out.println("第一次獲取數(shù)據(jù),未走緩存:"+id);
return user;
}
@PostMapping("updateUser")
public Boolean updateUser(@RequestBody User user){
// TODO 更新數(shù)據(jù)庫
userCache.put(user.getId(), user);
return true;
}
@PostMapping("deleteUser")
public Boolean deleteUser(Long id){
// TODO 從數(shù)據(jù)庫刪除
userCache.remove(id);
return true;
}
}
-
方式三 高級API模式:通過 CacheManager,2.7 版本才可使用
(1)添加依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.25</version>
</dependency>
(2)書寫配置類
@Configuration
public class JetcacheConfig {
@Autowired
private CacheManager cacheManager;
private Cache<Long, Object> userCache;
@PostConstruct
public void init(){
QuickConfig qc = QuickConfig.newBuilder("userCache:")
.expire(Duration.ofSeconds(3600))
.cacheType(CacheType.BOTH)
// 本地緩存更新后,將在所有的節(jié)點(diǎn)中刪除緩存,以保持強(qiáng)一致性
.syncLocal(false)
.build();
userCache = cacheManager.getOrCreateCache(qc);
}
@Bean
public Cache<Long, Object> getUserCache(){
return userCache;
}
}
(3)調(diào)用代碼
@RestController
@RequestMapping("user3")
public class User3Controller {
@Autowired
JetcacheConfig jetcacheConfig;
@Autowired
private Cache<Long, Object> userCache;
@GetMapping("get")
public User get(Long id){
if(userCache.get(id) != null){
return (User) userCache.get(id);
}
User user = new User();
user.setId(id);
user.setName("用戶both"+id);
user.setAge(23);
user.setSex(1);
userCache.put(id, user);
System.out.println("第一次獲取數(shù)據(jù),未走緩存:"+id);
return user;
}
@PostMapping("updateUser")
public Boolean updateUser(@RequestBody User user){
// TODO 更新數(shù)據(jù)庫
userCache.put(user.getId(), user);
return true;
}
@PostMapping("deleteUser")
public Boolean deleteUser(Long id){
// TODO 從數(shù)據(jù)庫刪除
userCache.remove(id);
return true;
}
}
多級緩存的形式,會先從本地緩存獲取數(shù)據(jù),本地獲取不到會從遠(yuǎn)程緩存獲取;關(guān)注工眾號:碼猿技術(shù)專欄,回復(fù)關(guān)鍵詞:1111 獲取阿里內(nèi)部Java性能調(diào)優(yōu)手冊!
5、啟動redis,啟動演示項目
注意,如果啟動出現(xiàn)
NoClassDefFoundError: redis/clients/util/Pool或NoClassDefFoundError: redis/clients/jedis/UnifiedJedis報錯,說明springboot與jetcache版本不一致,對應(yīng)關(guān)系可參考上述第一步中的說明 同時如果使用的是jetcache2.7.x版本,因為該版本中有jedis包的依賴,需要額外添加如下依賴,或者將jetcache版本將至2.6.5以下
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
3. 測試
3.1 方式一測試
1、訪問localhost:8088/user/getRemote?id=1
因為配置的是遠(yuǎn)程緩存,在redis中也能看到對應(yīng)的key
2、訪問localhost:8088/user/getLocal?id=1,這個方法是從本地緩存獲取的,現(xiàn)在只有遠(yuǎn)程緩存上有數(shù)據(jù),我們調(diào)用發(fā)現(xiàn)緩存數(shù)據(jù)還是拿到了,這說明當(dāng)我們在配置文件中配置了本地緩存和遠(yuǎn)程緩存后,方式一中本地緩存和遠(yuǎn)程緩存會自動相互調(diào)用
比如本地緩存有這個key,redis中沒有,通過遠(yuǎn)程緩存方式訪問時,會先從redis獲取,如果沒有會自動獲取本地緩存,但是數(shù)據(jù)還是存儲在本地緩存,并不會同步到redis上,這樣更加靈活的實(shí)現(xiàn)了多級緩存架構(gòu)
3.2 方式二測試
1、再測試下CreateCache的形式:localhost:8088/user2/get?id=4
正常獲取了,并且redis中也有了對應(yīng)的值
而當(dāng)我們把緩存方式更改為
LOCAL后,再訪問localhost:8088/user2/get?id=5
@CreateCache(name= "userCache:", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL)
會發(fā)現(xiàn)redis中就沒有對應(yīng)緩存了,只在本地緩存存在,說明我們指定本地緩存的形式成功了
3.3 方式三測試
1、調(diào)用localhost:8088/user3/get?id=11
redis中緩存設(shè)置成功!
4. 常見報錯
1、 ClassNotFoundException: com.alibaba.fastjson.JSON 解決:添加依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.25</version>
</dependency>
2、NoClassDefFoundError: redis/clients/jedis/UnifiedJedis 解決:添加依賴
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
或者將jetcache版本降低至2.6.5以下
