JetCache
JetCache是由阿里巴巴开源的通用缓存访问框架,如果你对Spring Cache很熟悉的话,请一定花一点时间了解一下JetCache,它更好用。
JetCache提供的核心能力包括:
1.1 常见的使用场景
public interface UserService {
@Cached(name="UserService.getUserById", expire = 3600)
User getUserById(long userId);
}
expire: 过期时间
CacheType : 缓存类型
@Cached注解,支持TTL(超时时间),CacheType有三种类型:LOCAL,REMOTE,BOTH
本地内存/远程/两级缓存,可根据情况合理选用,使用得当,可以减低Cache Server的压力以及服务器的响应时间。
1.2 方法缓存
public class ShopInfoProviderImpl implements ShopInfoProvider{
@Override
@Cached(name="shopServer.shop",key="#shopId",expire = 3600, cacheType = CacheType.REMOTE)
public Shop getShopById(Long shopId) {
return null;
}
@Override
@CacheUpdate(name="shopServer.shop",key="#shopId",value="#user")
public void modifyShop(Long shopId) {
}
@Override
@CacheInvalidate(name="shopServer.shop",key="#shopId")
public void removeShop(Long shopId) {
}
}
上面代码中可以看出,我们可以使用SpEL(Spring Expression Language)来设置key和Value,当入参是对象时,可以使用对象中的一个字段,如 #user.userId 来设置。name属性不是必须的,但是起个名字是个好习惯,展示统计数据的使用,会使用这个名字。如果同一个area两个@CreateCache的name配置一样,它们生成的Cache将指向同一个实例。这里面需要注意的是,java代码的编辑级别必须是1.8。
如果没有指定Key,那么系统会自动根据参数生成。
1.3 刷新缓存
@Override
@Cached(expire = 3600, cacheType = CacheType.REMOTE)
@CacheRefresh(refresh = 1800, stopRefreshAfterLastAccess = 3600, timeUnit =TimeUnit.SECONDS)
public void totalShop(Long shopId){
}
CacheType为REMOTE或者BOTH的时候,刷新行为是全局唯一的,也就是说,即使应用服务器是一个集群,也不会出现多个服务器同时去刷新同一个Key的情况。
一个Key的刷新任务,自该Key首次被访问后初始化,如果该Key部不被访问,在stopRefreshAfterLastAccess指定的时间后,相关的刷新任务就会被自动移除,这样避免了浪费资源去进行没有意义的刷新。
CachePenetrationProtect表示在多线程环境中同步加载数据。
1.4 实例缓存
@CreateCache(expire = 100)
private Cache<Long, ShopDO> shopCache;
用起来就像map
ShopDO shop = shopCache.get(123L);
shopCache.put(123L, shop);
shopCache.remove(123L);
创建一个两级(内存+远程)的缓存,内存中的元素个数限制在50个。
@CreateCache(name = "ShopServer.shopCache", expire = 100, cacheType = CacheType.BOTH, localLimit = 50)
private Cache<Long, ShopDO> shopCache;
1.5 基本配置(SpringBoot)
1)pom.xml
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.4.4</version>
</dependency>
2)application.yml更新:
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: linkedhashmap
keyConvertor: fastjson
remote:
default:
type: redis
keyConvertor: fastjson
valueEncoder: java
valueDecoder: java
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
host: 192.168.0.223
port: 6380
3)创建一个App类放在业务包得根下,EnableMethodCache,EnableCreateCacheAnnotation这两个注解分别激活Cached和CreateCache注解,其他和标准的SpringBoot程序是一样的,这个类可以直接main方法运行
package com.owner.im;
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
@EnableMethodCache(basePackages = "com.owner.im")
@EnableCreateCacheAnnotation
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4)验证结果
package com.owner.im.web;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import com.owner.im.model.ShopDO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class LoginController {
@CreateCache(expire = 300, cacheType = CacheType.LOCAL,timeUnit = TimeUnit.SECONDS)
private Cache<Long, ShopDO> shopCache;
@GetMapping("/login")
public String login(@RequestParam(name = "id") Long id, @RequestParam(name = "name") String name) {
System.out.println("请求参数:ID == " + id + "NAME == " + name);
ShopDO shopDO = new ShopDO();
shopDO.setId(id);
shopDO.setName(name);
shopCache.put(id, shopDO);
System.out.println("对象已放入,缓存有效期300s");
return shopCache.get(id).getId() + shopCache.get(id).getName();
}
@GetMapping("/getLoginInfo")
public void getLogin(@RequestParam(name = "id") Long id) {
ShopDO shopDO = shopCache.get(id);
if (null == shopDO) {
System.out.println("刷新后缓存失效");
}else{
System.out.println("刷新后缓存为:" + shopCache.get(id).getId() + shopCache.get(id).getName());
}
}
}
上图红框中内容必填。必须指定CacheType类型,否则会报错。300s后,重新访问方法2,缓存失效。
方法级别缓存:
@Override
@Cached(area = "activity-server", name = "getSpecialStartTimeRecent3Days", key = "'endTime:'+#endTime", expire = 1, timeUnit = TimeUnit.DAYS)
@CacheRefresh(refresh = 6, timeUnit = TimeUnit.HOURS)
public List<Timestamp> getSpecialStartTimeRecent3Days(Timestamp endTime) {
return activitySpecialDao.selectSpecialStartTimeRecent3Days(endTime);
}
以下代码,定义了缓存得area,name,key,过期时间。然后设定了定时刷新为6h.
方法缓存刷新:
@Override
@Cached(area = "activity-server", name = "getSpecialStartTimeRecent3Days", key = "'endTime:'+#endTime", expire = 1, timeUnit = TimeUnit.DAYS)
@CacheRefresh(refresh = 6, timeUnit = TimeUnit.HOURS)
public List<Timestamp> getSpecialStartTimeRecent3Days(Timestamp endTime) {
return activitySpecialDao.selectSpecialStartTimeRecent3Days(endTime);
}
@Override
@CacheInvalidate(area = "activity-server", name = "getSpecialStartTimeRecent3Days", key = "'endTime:'+#endTime")
public boolean freshSpecialStartTimeRecent3Days(Timestamp endTime){
logger.error("删除缓存getSpecialStartTimeRecent3Days");
return true;
}
方法级别缓存刷新,建议使方法缓存失效,不要用那个刷新;另外使用注解得形式JetCache是基于切面形式,加缓存与刷新缓存的注解必须在同一个类中,但是调用刷缓存的地方必须多封装一层,另外刷新缓存的入参必须与加缓存入参保持一致。
// 以下为调用缓存刷新的代码
SpecialFacade.java
public void updateSpecialTimes(){
DateTime yesterday = new DateTime().minusDays(1);
DateTime tomorrow = new DateTime().plus(1);
Timestamp startTime = Timestamp.valueOf(yesterday.toString("yyyy-MM-dd 00:00:00"));
Timestamp endTime = Timestamp.valueOf(tomorrow.toString("yyyy-MM-dd 23:59:59"));
activitySpecialService.freshSpecialStartTimeRecent3DayNoRealTime(startTime,endTime);
}