今天我们来介绍一下由Alibaba开源的一款缓存框架JetCache。其号称比Spring Cache用起来更加好用,笔者用了之后发现确实比较好用。
JetCache支持本地TTL
、两级缓存
和分布式缓存自动刷新
,开发者也可以手动的去操作缓存实例(类似于Redis/Map操作)。到目前为止共有4个组件可以使用:RedisCache
、TairCache
(未在github开源)、CaffeineCache
(内存)和简单的LinkedHashMapCache
(内存)。
<dependency>
<groupId>com.alicp.jetcachegroupId>
<artifactId>jetcache-starter-redisartifactId>
<version>2.5.3version>
dependency>
解决冲突
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.9.0version>
dependency>
@SpringBootApplication
@EnableMethodCache(basePackages = "com.moguhu.oc")
@EnableCreateCacheAnnotation
public class MySpringBootApp {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApp.class);
}
}
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: linkedhashmap
keyConvertor: fastjson
limit: 100
remote:
default:
type: redis
keyConvertor: fastjson
valueEncoder: java
valueDecoder: java
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
host: ${redis.host}
port: ${redis.port}
我们可以使用@Cached用在接口方法上面。如下所示:
public interface UserService {
@Cached(expire = 3600, cacheType = CacheType.REMOTE)
User getUserById(long userId);
}
expire表示返回值User对象,将在3600秒后过期,JetCache默认使用所有入参生成缓存的key。当然我们也可以手动指定key,如下所示:
public interface UserService {
@Cached(name="userCache-", key="#userId", expire = 3600)
User getUserById(long userId);
@CacheUpdate(name="userCache-", key="#user.userId", value="#user")
void updateUser(User user);
@CacheInvalidate(name="userCache-", key="#userId")
void deleteUser(long userId);
}
上面代码中可以看出,我们可以使用SpEL(Spring Expression Language)来设置key和Value,当入参是对象时,可以使用对象中的一个字段,如 #user.userId 来设置。name属性不是必须的,但是起个名字是个好习惯,展示统计数据的使用,会使用这个名字。如果同一个area两个@CreateCache的name配置一样,它们生成的Cache将指向同一个实例。这里面需要注意的是,java代码的编辑级别必须是1.8。
自动刷新
public interface SummaryService{
@Cached(expire = 3600, cacheType = CacheType.REMOTE)
@CacheRefresh(refresh = 1800, stopRefreshAfterLastAccess = 3600, timeUnit = TimeUnit.SECONDS)
@CachePenetrationProtect
BigDecimal summaryOfToday(long catagoryId);
}
CachePenetrationProtect表示在多线程环境中同步加载数据。
我们可以使用@CreateCache注解去创建一个Cache实例,默认超时时间是100秒:
@CreateCache(expire = 100, cacheType = CacheType.BOTH, localLimit = 50)
private Cache userCache;
上面代码创建了一个Cache实例。其中cacheType=CacheType.BOTH定义了一个2级缓存(本地和远程),其中本地使用了LRU Cache,上线是50个,操作是可以看做是一个Map,如下所示:
UserDO user = userCache.get(12345L);
userCache.put(12345L, loadUserFromDataBase(12345L));
userCache.remove(12345L);
userCache.computeIfAbsent(1234567L, (key) -> loadUserFromDataBase(1234567L));
当然我们也可以手动的去创建Cache实例,如下:
GenericObjectPoolConfig pc = new GenericObjectPoolConfig();
pc.setMinIdle(2);
pc.setMaxIdle(10);
pc.setMaxTotal(10);
JedisPool pool = new JedisPool(pc, "localhost", 6379);
Cache userCache = RedisCacheBuilder.createRedisCacheBuilder()
.keyConvertor(FastjsonKeyConvertor.INSTANCE)
.valueEncoder(JavaValueEncoder.INSTANCE)
.valueDecoder(JavaValueDecoder.INSTANCE)
.jedisPool(pool)
.keyPrefix("userCache-")
.expireAfterWrite(200, TimeUnit.SECONDS)
.buildCache();
CacheGetResult r = cache.GET(userId);
CompletionStage future = r.future();
future.thenRun(() -> {
if(r.isSuccess()){
System.out.println(r.getValue());
}
});
cache.tryLockAndRun("key", 60, TimeUnit.SECONDS, () -> heavyDatabaseOperation());
@CreateCache
@CacheRefresh(timeUnit = TimeUnit.MINUTES, refresh = 60, stopRefreshAfterLastAccess = 100)
@CachePenetrationProtect
private Cache orderSumCache;
@PostConstruct
public void init(){
orderSumCache.config().setLoader(this::loadOrderSumFromDatabase);
}
如果没有使用spring boot,可以按下面的方式配置(这里使用jedis客户端连接redis为例)。
<dependency>
<groupId>com.alicp.jetcachegroupId>
<artifactId>jetcache-annoartifactId>
<version>2.5.4version>
dependency>
<dependency>
<groupId>com.alicp.jetcachegroupId>
<artifactId>jetcache-redisartifactId>
<version>2.5.4version>
dependency>
配置了这个JetCacheConfig类以后,可以使用@CreateCache和@Cached注解。
import java.util.HashMap;
import java.util.Map;
import com.alicp.jetcache.anno.CacheConsts;
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import com.alicp.jetcache.anno.support.GlobalCacheConfig;
import com.alicp.jetcache.anno.support.SpringConfigProvider;
import com.alicp.jetcache.embedded.EmbeddedCacheBuilder;
import com.alicp.jetcache.embedded.LinkedHashMapCacheBuilder;
import com.alicp.jetcache.redis.RedisCacheBuilder;
import com.alicp.jetcache.support.FastjsonKeyConvertor;
import com.alicp.jetcache.support.JavaValueDecoder;
import com.alicp.jetcache.support.JavaValueEncoder;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.util.Pool;
@Configuration
@EnableMethodCache(basePackages = "com.company.mypackage")
@EnableCreateCacheAnnotation
public class JetCacheConfig {
@Bean
public Pool pool(){
GenericObjectPoolConfig pc = new GenericObjectPoolConfig();
pc.setMinIdle(2);
pc.setMaxIdle(10);
pc.setMaxTotal(10);
return new JedisPool(pc, "localhost", 6379);
}
@Bean
public SpringConfigProvider springConfigProvider() {
return new SpringConfigProvider();
}
@Bean
public GlobalCacheConfig config(SpringConfigProvider configProvider, Pool pool){
Map localBuilders = new HashMap();
EmbeddedCacheBuilder localBuilder = LinkedHashMapCacheBuilder
.createLinkedHashMapCacheBuilder()
.keyConvertor(FastjsonKeyConvertor.INSTANCE);
localBuilders.put(CacheConsts.DEFAULT_AREA, localBuilder);
Map remoteBuilders = new HashMap();
RedisCacheBuilder remoteCacheBuilder = RedisCacheBuilder.createRedisCacheBuilder()
.keyConvertor(FastjsonKeyConvertor.INSTANCE)
.valueEncoder(JavaValueEncoder.INSTANCE)
.valueDecoder(JavaValueDecoder.INSTANCE)
.jedisPool(pool);
remoteBuilders.put(CacheConsts.DEFAULT_AREA, remoteCacheBuilder);
GlobalCacheConfig globalCacheConfig = new GlobalCacheConfig();
globalCacheConfig.setConfigProvider(configProvider);
globalCacheConfig.setLocalCacheBuilders(localBuilders);
globalCacheConfig.setRemoteCacheBuilders(remoteBuilders);
globalCacheConfig.setStatIntervalMinutes(15);
globalCacheConfig.setAreaInCacheName(false);
return globalCacheConfig;
}
}
参考:《JetCache官方文档》
链接:http://moguhu.com/article/detail?articleId=95