jetcache(1)基本使用

基本使用

当前有四个实现,RedisCache、TairCache(此部分未在github开源)、CaffeineCache(in memory)和一个简易的LinkedHashMapCache(in memory),要添加新的实现也是非常简单的。

  • pom
<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>2.4.4</version>
</dependency>
  • 配置
jetcache.statIntervalMinutes: 15
jetcache.statIntervalMinutes: 15
jecache.local.default.type: linkedhashmap
jecache.local.default.keyConvertor: fastjson

jetcache.remote.default.type: redis
jetcache.remote.default.keyConvertor: fastjson
jetcache.remote.default.valueEncoder: java
jetcache.remote.default.valueDecoder: java
jetcache.remote.default.poolConfig.minIdle:5
jetcache.remote.default.poolConfig.maxIdle:20
jetcache.remote.default.poolConfig.maxTotal:50
jetcache.remote.default.host:127.0.0.1
jetcache.remote.default.port:6379
  • 注解

在启动类上加注解

@EnableMethodCache(basePackages="com.company.mypackage")
@EnableCreateCacheAnnotation
  • 缓存实例 @CreateCache
@CreateCache(expire = 100)
private Cache<Long, UserDO> userCache;

具体说明https://github.com/alibaba/jetcache/wiki/CreateCache_CN:

  • 方法缓存
    • @Cached注解可以为一个方法添加缓存
    • @CacheUpdate用于更新缓存
    • @CacheInvalidate用于移除缓存元素

注解可以加在接口上也可以加在类上,加注解的类必须是一个spring bean
具体说明https://github.com/alibaba/jetcache/wiki/MethodCache_CN:

@Configuration
public class JetCacheConfig {
    @Bean
    public SpringConfigProvider springConfigProvider() {
        return new SpringConfigProvider() {
            @Override
            public Consumer<StatInfo> statCallback() {
                return new Consumer<StatInfo>() {
                    @Override
                    public void accept(StatInfo stat) {
                        List<CacheStat> stats = stat.getStats();
                        if (stats != null && stats.size() > 0) {
                            for (CacheStat c : stats) {
                                System.out.println(DateUtil.getNow());
                                System.out.println("mycount............");
                                String info = "";
                                info += c.getCacheName() + ",";
                                info += c.getGetCount() + ",";
                                info += c.getGetHitCount() + ",";
                                info += c.getGetFailCount();
                                System.out.println(info);
                            }
                        }
                    }
                };
            }
        };
    }
}

自定义监控可以将监控信息输出到任何地方

jetchache默认的实现是:return new StatInfoLogger(false);
StatInfoLogger的构造参数设置为true会有更详细的统计信息,包括put等操作的统计。
StatInfoLogger输出的是给人读的信息,你也可以自定义logger将日志输出成特定格式,然后通过日志系统统一收集和统计。
如果想要让jetcache的日志输出到独立的文件中,在使用logback的情况下可以这样配置:

<appender name="JETCACHE_LOGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>jetcache.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>jetcache.log.%d{yyyy-MM-dd}</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>

    <encoder>
        <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

<logger name="com.alicp.jetcache" level="INFO" additivity="false">
    <appender-ref ref="JETCACHE_LOGFILE" />
</logger>

基本的cache api

  • javax.cache.Cache接口一致的方法
V get(K key)
void put(K key, V value);
boolean putIfAbsent(K key, V value); //多级缓存MultiLevelCache不支持此方法
boolean remove(K key);
<T> T unwrap(Class<T> clazz);//2.2版本前,多级缓存MultiLevelCache不支持此方法
Map<K,V> getAll(Set<? extends K> keys);
void putAll(Map<? extends K,? extends V> map);
void removeAll(Set<? extends K> keys);
  • 特有的方法

    V computeIfAbsent(K key, Function<K, V> loader)
     当key对应的缓存不存在时,使用loader加载。通过这种方式,loader的加载时间可以被统计到。
    
    V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull)
    当key对应的缓存不存在时,使用loader加载。cacheNullWhenLoaderReturnNull参数指定了当loader加载出来时null值的时候,是否要进行缓存(有时候即使是null值也是通过很繁重的查询才得到的,需要缓存)
    
    V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expire, TimeUnit timeUnit)
    当key对应的缓存不存在时,使用loader加载。cacheNullWhenLoaderReturnNull参数指定了当loader加载出来时null值的时候,是否要进行缓存(有时候即使是null值也是通过很繁重的查询才得到的,需要缓存)。expire和timeUnit指定了缓存的超时时间,会覆盖缓存的默认超时时间。
    
    void put(K key, V value, long expire, TimeUnit timeUnit)
    put操作,expire和timeUnit指定了缓存的超时时间,会覆盖缓存的默认超时时间。
    AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit) 
    boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action)
    非堵塞的尝试获取一个锁,如果对应的key还没有锁,返回一个AutoReleaseLock,否则立即返回空。如果Cache实例是本地的,它是一个本地锁,在本JVM中有效;如果是redis等远程缓存,它是一个不十分严格的分布式锁。锁的超时时间由expire和timeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。用法如下:
      // 使用try-with-resource方式,可以自动释放锁
    try(AutoReleaseLock lock = cache.tryLock("MyKey",100, TimeUnit.SECONDS)){
     if(lock != null){
        // do something
     }
    }
    

    上面的代码有个潜在的坑是忘记判断if(lock!=null),所以一般可以直接用tryLockAndRun更加简单

      boolean hasRun = tryLockAndRun("MyKey",100, TimeUnit.SECONDS), () -> {
    // do something
    };
    
  • 大写的方法
    V get(K key)这样的方法虽然用起来方便,但有功能上的缺陷,当get返回null的时候,无法断定是对应的key不存在,还是访问缓存发生了异常,所以JetCache针对部分操作提供了另外一套API,提供了完整的返回值,包括:
CacheGetResult<V> GET(K key);
MultiGetResult<K, V> GET_ALL(Set<? extends K> keys);
CacheResult PUT(K key, V value);
CacheResult PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
CacheResult PUT_ALL(Map<? extends K, ? extends V> map);
CacheResult PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit);
CacheResult REMOVE(K key);
CacheResult REMOVE_ALL(Set<? extends K> keys);
CacheResult PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);

这些方法的特征是方法名为大写,与小写的普通方法对应,提供了完整的返回值,用起来也稍微繁琐一些。例如:

CacheGetResult<OrderDO> r = cache.GET(orderId);
if( r.isSuccess() ){
    OrderDO order = r.getValue();
} else if (r.getResultCode() == CacheResultCode.NOT_EXISTS) {
    System.out.println("cache miss:" + orderId);
} else if(r.getResultCode() == CacheResultCode.EXPIRED) {
    System.out.println("cache expired:" + orderId));
} else {
    System.out.println("cache get error:" + orderId);
}

高级cache API

CacheBuilder
创建本地缓存
Cache<String, Integer> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();
创建redis缓存
GenericObjectPoolConfig pc = new GenericObjectPoolConfig();
        pc.setMinIdle(2);
        pc.setMaxIdle(10);
        pc.setMaxTotal(10);
        JedisPool pool = new JedisPool(pc, "localhost", 6379);
Cache<Long, OrderDO> orderCache = RedisCacheBuilder.createRedisCacheBuilder()
                .keyConvertor(FastjsonKeyConvertor.INSTANCE)
                .valueEncoder(JavaValueEncoder.INSTANCE)
                .valueDecoder(JavaValueDecoder.INSTANCE)
                .jedisPool(pool)
                .keyPrefix("orderCache")
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();
多级缓存
Cache multiLevelCache = MultiLevelCacheBuilder.createMultiLevelCacheBuilder()
      .addCache(memoryCache, redisCache)
      .expireAfterWrite(100, TimeUnit.SECONDS)
      .buildCache();
监控统计
Cache orderCache = ...
CacheMonitor orderCacheMonitor = new DefaultCacheMonitor("OrderCache");
orderCache.config().getMonitors().add(orderCacheMonitor); // jetcache 2.2+, or call builder.addMonitor() before buildCache()
// Cache<Long, Order> monitedOrderCache = new MonitoredCache(orderCache, orderCacheMonitor); //before jetcache 2.2
int resetTime = 1;
boolean verboseLog = false;
DefaultCacheMonitorManager cacheMonitorManager = new DefaultCacheMonitorManager(resetTime, TimeUnit.SECONDS, verboseLog);
cacheMonitorManager.add(orderCacheMonitor);
cacheMonitorManager.start();

==CacheBuilder提供使用代码直接构造Cache实例的方式,使用说明。如果没有使用Spring,可以使用CacheBuilder,否则没有必要直接使用CacheBuilder。==

异步api

从JetCache2.2版本开始,所有的大写API返回的CacheResult都支持异步。当底层的缓存实现支持异步的时候,大写API返回的结果都是异步的。当前支持异步的实现只有jetcache的redis-luttece实现,其他的缓存实现(内存中的、Tair、Jedis等),所有的异步接口都会同步堵塞,这样API仍然是兼容的。

以下的例子假设使用redis-luttece访问cache,例如:

CacheGetResult<UserDO> r = cache.GET(userId);
CompletionStage<ResultData> future = r.future();
future.thenRun(() -> {
    if(r.isSuccess()){
        System.out.println(r.getValue());
    }
});
自动load

LoadingCache类提供了自动load的功能,它是一个包装,基于decorator模式,也实现了Cache接口。如果CacheBuilder指定了loader,那么buildCache返回的Cache实例就是经过LoadingCache包装过的。例如:

Cache<Long,UserDO> userCache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .loader(key -> loadUserFromDatabase(key))
                .buildCache();

LoadingCache的get和getAll方法,在缓存未命中的情况下,会调用loader,如果loader抛出一场,get和getAll会抛出CacheInvokeException。

注意:
1. GET、GET_ALL这类大写API只纯粹访问缓存,不会调用loader。
2. 如果使用多级缓存,loader应该安装在MultiLevelCache上,不要安装在底下的缓存上。

==springboot自动load==:注解的属性只能是常量,所以没有办法在CreateCache注解中指定loader,不过我们可以这样:

@CreateCache
private Cache<Long,UserDO> userCache;

@PostConstruct
public void init(){
    userCache.config().setLoader(this::loadUserFromDatabase);
}
自动刷新缓存

从JetCache2.2版本开始,RefreshCache基于decorator模式提供了自动刷新的缓存的能力,目的是为了防止缓存失效时造成的雪崩效应打爆数据库。同时设置了loader和refreshPolicy的时候,CacheBuilder的buildCache方法返回的Cache实例经过了RefreshCache的包装。

RefreshPolicy policy = RefreshPolicy.newPolicy(1, TimeUnit.MINUTES)
                .stopRefreshAfterLastAccess(30, TimeUnit.MINUTES);
Cache<String, Long> orderSumCache = LinkedHashMapCacheBuilder
                .createLinkedHashMapCacheBuilder()
                .loader(key -> loadOrderSumFromDatabase(key))
                .refreshPolicy(policy)
                .buildCache();

上面的代码指定每分钟刷新一次,30分钟如果没有访问就停止刷新。如果缓存是redis或者多级缓存最后一级是redis,缓存加载行为是全局唯一的,也就是说不管有多少台服务器,同时只有一个服务器在刷新,这是通过tryLock实现的,目的是为了降低后端的加载负担。

==springboot自动刷新缓存==, 与LoadingCache一样,使用@CreateCache时,我们需要这样来添加自动刷新功能

@CreateCache
private Cache<String, Long> orderSumCache;

@PostConstruct
public void init(){
    RefreshPolicy policy = RefreshPolicy.newPolicy(1, TimeUnit.MINUTES)
                          .stopRefreshAfterLastAccess(30, TimeUnit.MINUTES);
    orderSumCache.config().setLoader(this::loadOrderSumFromDatabase);
    orderSumCache.config().setRefreshPolicy(policy);
}
### 关于 JetCache使用方法及教程 JetCache 是一款用于 Java 应用程序的缓存框架,提供了简便易用的 API 和注解支持来简化开发人员的操作[^3]。 #### 添加依赖项 为了在 Spring Boot 中集成并使用 JetCache 实现二级缓存机制,需先引入必要的库文件。具体来说,在项目的 `pom.xml` 文件中添加如下 Maven 依赖: ```xml <dependency> <groupId>com.alicloud.openservices</groupId> <artifactId>jetcache-starter-redis-spring-boot</artifactId> <version>${latest.version}</version> </dependency> <!-- 如果还需要 Hazelcast 支持 --> <dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast</artifactId> <version>${hazelcast.version}</version> </dependency> ``` 上述配置使得应用程序能够利用 Redis 及 Hazelcast 进行分布式环境下的高效数据存储与访问[^2]。 #### 注解驱动的缓存管理 通过简单的注解即可完成复杂的缓存逻辑定义。以下是几个常用的注解及其作用说明: - **@Cached**: 将查询结果自动保存到指定名称的空间里;当再次请求相同参数的数据时会优先读取本地缓存副本而非重新计算。 - **@CacheUpdate**: 更新已存在的键对应的值或者创建新的记录条目。 - **@CacheInvalidate**: 移除特定条件匹配的所有缓存项,适用于批量清理场景。 这些特性极大地提高了应用性能的同时也降低了数据库的压力[^1]。 #### 缓存策略设置 除了基本 CRUD 功能外,还可以自定义更多高级选项比如过期时间、加载器函数等。下面给出一段完整的代码片段展示如何声明带有限制条件的方法级缓存行为: ```java import com.alicloud.openservices.tablestore.model.search.query.Query; import org.springframework.cache.annotation.Cacheable; public interface UserService { /** * 获取用户信息. */ @Cached(name = "user", key = "#id", localExpire = 60, remoteExpire = 300) User getUserById(Long id); } ``` 此例子中的 `localExpire` 参数指定了本地内存缓存的有效期限为 60 秒而远程持久化层则保持更长时间即 300 秒不变。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值