天天看點

jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

jetcache

簡介

JetCache是一個基于Java的緩存系統封裝,提供統一的API和注解來簡化緩存的使用。 JetCache提供了比SpringCache更加強大的注解,可以原生的支援TTL、兩級緩存、分布式自動重新整理,還提供了

Cache

接口用于手工緩存操作。目前有四個實作,

RedisCache

TairCache

(此部分未在github開源)、

CaffeineCache

(in memory)和一個簡易的

LinkedHashMapCache

(in memory),要添加新的實作也是非常簡單的。

全部特性:

  • 通過統一的API通路Cache系統
  • 通過注解實作聲明式的方法緩存,支援TTL和兩級緩存
  • 通過注解建立并配置

    Cache

    執行個體
  • 針對所有

    Cache

    執行個體和方法緩存的自動統計
  • Key的生成政策和Value的序列化政策是可以配置的
  • 分布式緩存自動重新整理,分布式鎖 (2.2+)
  • 異步Cache API (2.2+,使用Redis的lettuce用戶端時)
  • Spring Boot支援

要求

JetCache需要JDK1.8、Spring Framework4.0.8以上版本。Spring Boot為可選,需要1.1.9以上版本。如果不使用注解(僅使用jetcache-core),Spring Framework也是可選的,此時使用方式與Guava/Caffeinecache類似。

文檔目錄

    • 使用 jedis用戶端連接配接redis
    • lettuce用戶端連接配接redis

依賴哪個jar?

  • jetcache-anno-api:定義jetcache的注解和常量,不傳遞依賴。如果你想把Cached注解加到接口上,又不希望你的接口jar傳遞太多依賴,可以讓接口jar依賴jetcache-anno-api。
  • jetcache-core:核心api,完全通過程式設計來配置操作

    Cache

    ,不依賴Spring。兩個記憶體中的緩存實作

    LinkedHashMapCache

    CaffeineCache

    也由它提供。
  • jetcache-anno:基于Spring提供@Cached和@CreateCache注解支援。
  • jetcache-redis:使用jedis提供Redis支援。
  • jetcache-redis-lettuce(需要JetCache2.3以上版本):使用lettuce提供Redis支援,實作了JetCache異步通路緩存的的接口。
  • jetcache-starter-redis:Spring Boot方式的Starter,基于Jedis。
  • jetcache-starter-redis-lettuce(需要JetCache2.3以上版本):Spring Boot方式的Starter,基于Lettuce。

建立緩存執行個體

通過@CreateCache注解建立一個緩存執行個體,預設逾時時間是100秒

@CreateCache(expire = 100)
private Cache<Long, UserDO> userCache;      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

用起來就像map一樣

UserDO user = userCache.get(123L);
userCache.put(123L, user);
userCache.remove(123L);      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

建立一個兩級(記憶體+遠端)的緩存,記憶體中的元素個數限制在50個。

@CreateCache(name = "UserService.userCache", expire = 100, cacheType = CacheType.BOTH, localLimit = 50)
private Cache<Long, UserDO> userCache;      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

name屬性不是必須的,但是起個名字是個好習慣,展示統計資料的使用,會使用這個名字。如果同一個area兩個@CreateCache的name配置一樣,它們生成的Cache将指向同一個執行個體。

建立方法緩存

使用@Cached方法可以為一個方法添加上緩存。JetCache通過Spring AOP生成代理,來支援緩存功能。注解可以加在接口方法上也可以加在類方法上,但需要保證是個Springbean。

public interface UserService {
    @Cached(name="UserService.getUserById", expire = 3600)
    User getUserById(long userId);
}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

基本配置(使用Spring Boot)

如果使用SpringBoot,可以按如下的方式配置。

POM

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>2.4.4</version>
</dependency>      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

配置一個springboot風格的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: 127.0.0.1
      port: 6379      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

然後建立一個App類放在業務包的根下,EnableMethodCache,EnableCreateCacheAnnotation這兩個注解分别激活Cached和CreateCache注解,其他和标準的SpringBoot程式是一樣的。這個類可以直接main方法運作。

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
@EnableMethodCache(basePackages = "com.company.mypackage")
@EnableCreateCacheAnnotation
public class MySpringBootApp {
    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApp.class);
    }
}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

未使用SpringBoot的配置方式

如果沒有使用springboot,可以按下面的方式配置(這裡使用jedis用戶端連接配接redis為例)。

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-anno</artifactId>
    <version>2.4.4</version>
</dependency>
<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-redis</artifactId>
    <version>2.4.4</version>
</dependency>      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

配置了這個JetCacheConfig類以後,可以使用@CreateCache和@Cached注解。

package com.company.mypackage;
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<Jedis> 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<Jedis> 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教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

進一步閱讀

  • CreateCache的詳細使用說明可以看 這裡
  • 使用@CacheCache建立的Cache接口執行個體,它的API使用可以看
  • 關于方法緩存(@Cached, @CacheUpdate, @CacheInvalidate)的詳細使用看
  • 詳細的配置說明看

基本Cache API

JetCache2.0的核心是

com.alicp.jetcache.Cache

接口(以下簡寫為

Cache

),它提供了部分類似于

javax.cache.Cache

(JSR107)的API操作。沒有完整實作JSR107的原因包括:

  1. 希望維持API的簡單易用。
  2. 對于特定的遠端緩存系統來說,

    javax.cache.Cache

    中定義的有些操作無法高效率的實作,比如一些原子操作方法和類似

    removeAll()

    這樣的方法。
  3. JSR107比較複雜,完整實作要做的工作很多。

JSR107 style API

以下是Cache接口中和JSR107的javax.cache.Cache接口一緻的方法,除了不會抛出異常,這些方法的簽名和行為和JSR107都是一樣的。

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);      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

JetCache特有API

V computeIfAbsent(K key, Function<K, V> loader)      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

當key對應的緩存不存在時,使用loader加載。通過這種方式,loader的加載時間可以被統計到。

V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull)      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

當key對應的緩存不存在時,使用loader加載。cacheNullWhenLoaderReturnNull參數指定了當loader加載出來時null值的時候,是否要進行緩存(有時候即使是null值也是通過很繁重的查詢才得到的,需要緩存)。

V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expire, TimeUnit timeUnit)      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

當key對應的緩存不存在時,使用loader加載。cacheNullWhenLoaderReturnNull參數指定了當loader加載出來時null值的時候,是否要進行緩存(有時候即使是null值也是通過很繁重的查詢才得到的,需要緩存)。expire和timeUnit指定了緩存的逾時時間,會覆寫緩存的預設逾時時間。

void put(K key, V value, long expire, TimeUnit timeUnit)      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

put操作,expire和timeUnit指定了緩存的逾時時間,會覆寫緩存的預設逾時時間。

AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit)      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action)      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

非堵塞的嘗試擷取一個鎖,如果對應的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
     }
  }      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

上面的代碼有個潛在的坑是忘記判斷if(lock!=null),是以一般可以直接用tryLockAndRun更加簡單

boolean hasRun = tryLockAndRun("MyKey",100, TimeUnit.SECONDS), () -> {
    // do something
  };      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

tryLock内部會在通路遠端緩存失敗時重試,會自動釋放,而且不會釋放不屬于自己的鎖,比你自己做這些要簡單。當然,基于遠端緩存實作的任何分布式鎖都不會是嚴格的分布式鎖,不能和基于ZooKeeper或Consul做的鎖相比。

大寫API

Vget(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);      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

這些方法的特征是方法名為大寫,與小寫的普通方法對應,提供了完整的傳回值,用起來也稍微繁瑣一些。例如:

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);
}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

通過@CreateCache注解建立Cache執行個體

在Springbean中使用@CreateCache注解建立一個Cache執行個體。例如

@CreateCache(expire = 100)
private Cache<Long, UserDO> userCache;      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

@CreateCache屬性表

屬性 預設值 說明
area “default” 如果需要連接配接多個緩存系統,可在配置多個cache area,這個屬性指定要使用的那個area的name
name 未定義 指定緩存的名稱,不是必須的,如果沒有指定,會使用類名+方法名。name會被用于遠端緩存的key字首。另外在統計中,一個簡短有意義的名字會提高可讀性。如果兩個

@CreateCache

name

area

相同,它們會指向同一個

Cache

expire 該Cache執行個體的預設逾時時間定義,注解上沒有定義的時候會使用全局配置,如果此時全局配置也沒有定義,則取無窮大
timeUnit TimeUnit.SECONDS 指定expire的機關
cacheType CacheType.REMOTE 緩存的類型,包括CacheType.REMOTE、CacheType.LOCAL、CacheType.BOTH。如果定義為BOTH,會使用LOCAL和REMOTE組合成兩級緩存
localLimit 如果cacheType為CacheType.LOCAL或CacheType.BOTH,這個參數指定本地緩存的最大元素數量,以控制記憶體占用。注解上沒有定義的時候會使用全局配置,如果此時全局配置也沒有定義,則取100
serialPolicy 如果cacheType為CacheType.REMOTE或CacheType.BOTH,指定遠端緩存的序列化方式。JetCache内置的可選值為SerialPolicy.JAVA和SerialPolicy.KRYO。注解上沒有定義的時候會使用全局配置,如果此時全局配置也沒有定義,則取SerialPolicy.JAVA
keyConvertor 指定KEY的轉換方式,用于将複雜的KEY類型轉換為緩存實作可以接受的類型,JetCache内置的可選值為KeyConvertor.FASTJSON和KeyConvertor.NONE。NONE表示不轉換,FASTJSON通過fastjson将複雜對象KEY轉換成String。如果注解上沒有定義,則使用全局配置。

對于以上未定義預設值的參數,如果沒有指定,将使用yml中指定的全局配置,請參考

配置

詳解。

JetCache方法緩存和SpringCache比較類似,它原生提供了TTL支援,以保證最終一緻,并且支援二級緩存。JetCache2.4以後支援基于注解的緩存更新和删除。

在spring環境下,使用@Cached注解可以為一個方法添加緩存,@CacheUpdate用于更新緩存,@CacheInvalidate用于移除緩存元素。注解可以加在接口上也可以加在類上,加注解的類必須是一個spring bean,例如:

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);
}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

key使用Spring的

SpEL

腳本來指定。如果要使用參數名(比如這裡的key="#userId"),項目編譯設定target必須為1.8格式,并且指定javac的-parameters參數,否則就要使用key="args[0]"這樣按下标通路的形式。

@CacheUpdate和@CacheInvalidate的name和area屬性必須和@Cached相同,name屬性還會用做cache的key字首。

@Cached注解和@CreateCache的屬性非常類似,但是多幾個:

如果在配置中配置了多個緩存area,在這裡指定使用哪個area
指定緩存的唯一名稱,不是必須的,如果沒有指定,會使用類名+方法名。name會被用于遠端緩存的key字首。另外在統計中,一個簡短有意義的名字會提高可讀性。
key 指定key,如果沒有指定會根據所有參數自動生成。
逾時時間。如果注解上沒有定義,會使用全局配置,如果此時全局配置也沒有定義,則為無窮大
如果cacheType為LOCAL或BOTH,這個參數指定本地緩存的最大元素數量,以控制記憶體占用。如果注解上沒有定義,會使用全局配置,如果此時全局配置也沒有定義,則為100
指定遠端緩存的序列化方式。可選值為SerialPolicy.JAVA和SerialPolicy.KRYO。如果注解上沒有定義,會使用全局配置,如果此時全局配置也沒有定義,則為SerialPolicy.JAVA
指定KEY的轉換方式,用于将複雜的KEY類型轉換為緩存實作可以接受的類型,目前支援KeyConvertor.FASTJSON和KeyConvertor.NONE。NONE表示不轉換,FASTJSON可以将複雜對象KEY轉換成String。如果注解上沒有定義,會使用全局配置。
enabled true 是否激活緩存。例如某個dao方法上加緩存注解,由于某些調用場景下不能有緩存,是以可以設定enabled為false,正常調用不會使用緩存,在需要的地方可使用CacheContext.enableCache在回調中激活緩存,緩存激活的标記在ThreadLocal上,該标記被設定後,所有enable=false的緩存都被激活
cacheNullValue false 當方法傳回值為null的時候是否要緩存
condition 指定條件,如果表達式傳回true的時候才進行緩存

@CacheInvalidate注解說明:

如果在配置中配置了多個緩存area,在這裡指定使用哪個area,指向對應的@Cached定義。
指定緩存的唯一名稱,指向對應的@Cached定義。
指定key
指定條件,如果表達式傳回true才執行删除

@CacheUpdate注解說明:

value 指定value
指定條件,如果表達式傳回true才執行更新

使用@CacheUpdate和@CacheInvalidate的時候,相關的緩存操作可能會失敗(比如網絡IO錯誤),是以指定緩存的逾時時間是非常重要的。

@CacheRefresh注解說明:

refresh 重新整理間隔
時間機關
stopRefreshAfterLastAccess 指定該key多長時間沒有通路就停止重新整理,如果不指定會一直重新整理
refreshLockTimeout 60秒 類型為BOTH/REMOTE的緩存重新整理時,同時隻會有一台伺服器在重新整理,這台伺服器會在遠端緩存放置一個分布式鎖,此配置指定該鎖的逾時時間

對于以上未定義預設值的參數,如果沒有指定,将使用yml中指定的全局配置,全局配置請參考

配置說明

yml配置檔案案例(如果沒使用springboot,直接配置GlobalCacheConfig是類似的,參考快速入門教程):

jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  hiddenPackages: com.alibaba
  local:
    default:
      type: caffeine
      limit: 100
      keyConvertor: fastjson
      expireAfterWriteInMillis: 100000
    otherArea:
      type: linkedhashmap
      limit: 100
      keyConvertor: none
      expireAfterWriteInMillis: 100000
  remote:
    default:
      type: redis
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${redis.host}
      port: ${redis.port}
    otherArea:
      type: redis
      keyConvertor: fastjson
      valueEncoder: kryo
      valueDecoder: kryo
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${redis.host}
      port: ${redis.port}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

配置通用說明如下

jetcache.statIntervalMinutes 統計間隔,0表示不統計
jetcache.areaInCacheName jetcache-anno把cacheName作為遠端緩存key字首,2.4.3以前的版本總是把areaName加在cacheName中,是以areaName也出現在key字首中。2.4.4以後可以配置,為了保持遠端key相容預設值為true,但是新項目的話false更合理些。
jetcache.hiddenPackages @Cached和@CreateCache自動生成name的時候,為了不讓name太長,hiddenPackages指定的包名字首被截掉
jetcache.[local|remote].${area}.type 緩存類型。tair、redis為目前支援的遠端緩存;linkedhashmap、caffeine為目前支援的本地緩存類型
jetcache.[local|remote].${area}.keyConvertor key轉換器的全局配置,目前隻有一個已經實作的keyConvertor:fastjson。僅當使用@CreateCache且緩存類型為LOCAL時可以指定為none,此時通過equals方法來識别key。方法緩存必須指定keyConvertor
jetcache.[local|remote].${area}.valueEncoder java 序列化器的全局配置。僅remote類型的緩存需要指定,可選java和kryo
jetcache.[local|remote].${area}.valueDecoder
jetcache.[local|remote].${area}.limit 100 每個緩存執行個體的最大元素的全局配置,僅local類型的緩存需要指定。注意是每個緩存執行個體的限制,而不是全部,比如這裡指定100,然後用@CreateCache建立了兩個緩存執行個體(并且注解上沒有設定localLimit屬性),那麼每個緩存執行個體的限制都是100
jetcache.[local|remote].${area}.expireAfterWriteInMillis 無窮大 以毫秒為機關指定逾時時間的全局配置(以前為defaultExpireInMillis)
jetcache.local.${area}.expireAfterAccessInMillis 需要jetcache2.2以上,以毫秒為機關,指定多長時間沒有通路,就讓緩存失效,目前隻有本地緩存支援。0表示不使用這個功能。

上表中${area}對應@Cached和@CreateCache的area屬性。注意如果注解上沒有指定area,預設值是"default"。

關于緩存的逾時時間,有多個地方指定,澄清說明一下:

  1. put等方法上指定了逾時時間,則以此時間為準
  2. put等方法上未指定逾時時間,使用Cache執行個體的預設逾時時間
  3. Cache執行個體的預設逾時時間,通過在@CreateCache和@Cached上的expire屬性指定,如果沒有指定,使用yml中定義的全局配置,例如@Cached(cacheType=local)使用jetcache.local.default.expireAfterWriteInMillis,如果仍未指定則是無窮大

進階Cache API

CacheBuilder

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);      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

這一行代碼執行完以後,緩存操作可能還沒有完成,如果此時調用r.isSuccess()或者r.getValue()或者r.getMessage()将會堵塞直到緩存操作完成。如果不想被堵塞,并且需要在緩存操作完成以後執行後續操作,可以這樣做:

CompletionStage<ResultData> future = r.future();
future.thenRun(() -> {
    if(r.isSuccess()){
        System.out.println(r.getValue());
    }
});      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

以上代碼将會在緩存操作異步完成後,在完成異步操作的線程中調用thenRun中指定的回調。CompletionStage是Java8新增的功能,如果對此不太熟悉可以先查閱相關的文檔。需要注意的是,既然已經選擇了異步的開發方式,在回調中不能調用堵塞方法,以免堵塞其他的線程(回調方法很可能是在event loop線程中執行的)。

部分小寫的api不需要任何修改,就可以直接享受到異步開發的好處。比如put和removeAll方法,由于它們沒有傳回值,是以此時就直接優化成異步調用,能夠減少RT;而get方法由于需要取傳回值,是以仍然會堵塞。

自動load(read through)

LoadingCache類提供了自動load的功能,它是一個包裝,基于decorator模式,也實作了Cache接口。如果CacheBuilder指定了loader,那麼buildCache傳回的Cache執行個體就是經過LoadingCache包裝過的。例如:

Cache<Long,UserDO> userCache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .loader(key -> loadUserFromDatabase(key))
                .buildCache();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

LoadingCache的get和getAll方法,在緩存未命中的情況下,會調用loader,如果loader抛出一場,get和getAll會抛出CacheInvokeException。

需要注意

  1. GET、GET_ALL這類大寫API隻純粹通路緩存,不會調用loader。
  2. 如果使用多級緩存,loader應該安裝在MultiLevelCache上,不要安裝在底下的緩存上。

注解的屬性隻能是常量,是以沒有辦法在CreateCache注解中指定loader,不過我們可以這樣:

@CreateCache
private Cache<Long,UserDO> userCache;
@PostConstruct
public void init(){
    userCache.config().setLoader(this::loadUserFromDatabase);
}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

@CreateCache總是初始化一個經過LoadingCache包裝的Cache,直接在config中設定loader,可以實時生效。

自動重新整理緩存

從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();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

對一些key比較少,實時性要求不高,加載開銷非常大的緩存場景,适合使用自動重新整理。上面的代碼指定每分鐘重新整理一次,30分鐘如果沒有通路就停止重新整理。如果緩存是redis或者多級緩存最後一級是redis,緩存加載行為是全局唯一的,也就是說不管有多少台伺服器,同時隻有一個伺服器在重新整理,這是通過tryLock實作的,目的是為了降低後端的加載負擔。

與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快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

使用jedis用戶端連接配接redis

redis有多種

java版本的用戶端,JetCache2.2以前使用jedis用戶端通路redis。從JetCache2.2版本開始,增加了對luttece用戶端的支援,jetcache的luttece支援提供了異步操作和redis叢集支援。

如果選用jedis通路redis,對應的maven artifact是jetcache-redis和jetcache-starter-redis(spring boot)。

spring boot環境下的jedis支援

application.yml檔案如下(這裡省去了

local相關的配置):

jetcache: 
  areaInCacheName: false
  remote:
    default:
      type: redis
      keyConvertor: fastjson
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${redis.host}
      port: ${redis.port}
      #sentinels: 127.0.0.1:26379 , 127.0.0.1:26380, 127.0.0.1:26381
      #masterName: mymaster      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
如果需要直接操作JedisPool,可以通過以下方式擷取
@Bean(name = "defaultPool")
@DependsOn(RedisAutoConfiguration.AUTO_INIT_BEAN_NAME)//jetcache2.2+
//@DependsOn("redisAutoInit")//jetcache2.1
public JedisPoolFactory defaultPool() {
    return new JedisPoolFactory("remote.default", JedisPool.class);
}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
然後可以直接使用
@Autowired
private Pool<Jedis> defaultPool;      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
也可以用

Cache

接口上的

<T> Tunwrap(Class<T> clazz)

方法來擷取JedisPool,參見RedisCache.unwrap源代碼。

不使用spring boot

@Configuration
@EnableMethodCache(basePackages = "com.company.mypackage")
@EnableCreateCacheAnnotation
public class JetCacheConfig {
    @Bean
    public Pool<Jedis> pool(){
        // build jedis pool ...
    }
    @Bean
    public SpringConfigProvider springConfigProvider() {
        return new SpringConfigProvider();
    }
    @Bean
    public GlobalCacheConfig config(SpringConfigProvider configProvider, Pool<Jedis> 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教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

Builder API

如果不通過@CreateCache和@Cached注解,可以通過下面的方式建立RedisCache。通過注解建立的緩存會自動設定keyPrefix,這裡是手工建立緩存,對于遠端緩存需要設定keyPrefix屬性,以免不同Cache執行個體的key發生沖突。
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();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

常見問題

如果遇到這個錯誤
java.lang.NoSuchMethodError: redis.clients.jedis.JedisPool.<init>(Lorg/apache/commons/pool2/impl/GenericObjectPoolConfig;Ljava/lang/String;IILjava/lang/String;ILjava/lang/String;Z)V      
請確定jedis的版本在2.9.0以上,spring boot 1.5以下版本的spring-boot-dependencies會引入較低版本的jedis,可以在自己的pom中強制直接依賴jedis版本2.9.0:
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

使用lettuce用戶端連接配接redis

java版本的用戶端,JetCache2.2以前使用jedis用戶端通路redis。從JetCache2.2版本開始,增加了對lettuce用戶端的支援,JetCache的lettuce支援提供了異步操作和redis叢集支援。

使用lettuce通路redis,對應的maven artifact是jetcache-redis-lettuce和jetcache-starter-redis-lettuce。lettuce使用Netty建立單個連接配接連redis,是以不需要配置連接配接池。 注意:新釋出的lettuce5更換了groupId和包名,2.3版本的JetCache同時支援lettuce4和5,jetcache-redis-lettuce,jetcache-starter-redis-lettuce提供lettuce5支援,jetcache-redis-lettuce4和jetcache-starter-redis-lettuce4提供lettuce4支援。 注意:JetCache2.2版本中,lettuce單詞存在錯誤的拼寫,錯寫為“luttece”,該錯誤存在于包名、類名和配置中,2.3已經改正。

spring boot環境下的lettuce支援

jetcache: 
  areaInCacheName: false
  remote:
    default:
      type: redis.lettuce
      keyConvertor: fastjson
      uri: redis://127.0.0.1:6379/      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
如果使用sentinel做自動主備切換,uri可以配置為redis-sentinel://127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381/?sentinelMasterId=mymaster 如果是叢集:
jetcache: 
  areaInCacheName: false
  remote:
    default:
      type: redis.lettuce
      keyConvertor: fastjson
      uri:
        - redis://127.0.0.1:7000
        - redis://127.0.0.1:7001
        - redis://127.0.0.1:7002      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
如果需要直接使用lettuce的RedisClient:
@Bean(name = "defaultClient")
@DependsOn(RedisLettuceAutoConfiguration.AUTO_INIT_BEAN_NAME)
public LettuceFactory defaultClient() {
    return new LettuceFactory("remote.default", RedisClient.class);
}      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南
@Autowired
private RedisClient defaultClient;      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

Cache

<T> Tunwrap(Class<T> clazz)

方法來擷取

RedisClient

RedisCommands

等。參考RedisLettuceCache.unwrap源代碼。

@Configuration
@EnableMethodCache(basePackages = "com.company.mypackage")
@EnableCreateCacheAnnotation
public class JetCacheConfig {
    @Bean
    public RedisClient redisClient(){
        RedisClient client = RedisClient.create("redis://127.0.0.1");
        return client;
    }
    @Bean
    public SpringConfigProvider springConfigProvider() {
        return new SpringConfigProvider();
    }
    @Bean
    public GlobalCacheConfig config(SpringConfigProvider configProvider,RedisClient redisClient){
        Map localBuilders = new HashMap();
        EmbeddedCacheBuilder localBuilder = LinkedHashMapCacheBuilder
                .createLinkedHashMapCacheBuilder()
                .keyConvertor(FastjsonKeyConvertor.INSTANCE);
        localBuilders.put(CacheConsts.DEFAULT_AREA, localBuilder);
        Map remoteBuilders = new HashMap();
        RedisLettuceCacheBuilder remoteCacheBuilder = RedisLettuceCacheBuilder.createRedisLettuceCacheBuilder()
                .keyConvertor(FastjsonKeyConvertor.INSTANCE)
                .valueEncoder(JavaValueEncoder.INSTANCE)
                .valueDecoder(JavaValueDecoder.INSTANCE)
                .redisClient(redisClient);
        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教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

builder API

如果不通過@CreateCache和@Cached注解,可以通過下面的方式建立Cache。通過注解建立的緩存會自動設定keyPrefix,這裡是手工建立緩存,對于遠端緩存需要設定keyPrefix屬性,以免不同Cache執行個體的key發生沖突。
RedisClient client = RedisClient.create("redis://127.0.0.1");
Cache<Long,OrderDO> orderCache = RedisLettuceCacheBuilder.createRedisLettuceCacheBuilder()
                .keyConvertor(FastjsonKeyConvertor.INSTANCE)
                .valueEncoder(JavaValueEncoder.INSTANCE)
                .valueDecoder(JavaValueDecoder.INSTANCE)
                .redisClient(client)
                .keyPrefix("orderCache")
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

記憶體緩存LinkedHashMapCache和CaffeineCache

本地緩存目前有兩個實作。如果自己用jetcache-core的Cache API,可以不指定keyConvertor,此時本地緩存使用equals方法來比較key。如果使用jetcache-anno中的@Cached、@CreateCache等注解,必須指定keyConvertor。

LinkedHashMapCache

LinkedHashMapCache是

JetCache中實作的一個最簡單的Cache,使用LinkedHashMap做LRU方式淘汰。

Cache<Long, OrderDO> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

CaffeineCache

caffeine cache的介紹看

這裡,它是guava cache的後續作品。

Cache<Long, OrderDO> cache = CaffeineCacheBuilder.createCaffeineCacheBuilder()
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

當yml中的jetcache.statIntervalMinutes大于0時,通過@CreateCache和@Cached配置出來的Cache自帶監控。JetCache會按指定的時間定期通過logger輸出統計資訊。預設輸出資訊類似如下:

2017-01-12 19:00:00,001 INFO  support.StatInfoLogger - jetcache stat from 2017-01-12 18:59:00,000 to 2017-01-12 19:00:00,000
cache                                                |       qps|   rate|           get|           hit|          fail|        expire|avgLoadTime|maxLoadTime
-----------------------------------------------------+----------+-------+--------------+--------------+--------------+--------------+-----------+-----------
default_AlicpAppChannelManager.getAlicpAppChannelById|      0.00|  0.00%|             0|             0|             0|             0|        0.0|          0
default_ChannelManager.getChannelByAccessToten       |     30.02| 99.78%|         1,801|         1,797|             0|             4|        0.0|          0
default_ChannelManager.getChannelByAppChannelId      |      8.30| 99.60%|           498|           496|             0|             1|        0.0|          0
default_ChannelManager.getChannelById                |      6.65| 98.75%|           399|           394|             0|             4|        0.0|          0
default_ConfigManager.getChannelConfig               |      1.97| 96.61%|           118|           114|             0|             4|        0.0|          0
default_ConfigManager.getGameConfig                  |      0.00|  0.00%|             0|             0|             0|             0|        0.0|          0
default_ConfigManager.getInstanceConfig              |     43.98| 99.96%|         2,639|         2,638|             0|             0|        0.0|          0
default_ConfigManager.getInstanceConfigSettingMap    |      2.45| 70.75%|           147|           104|             0|            43|        0.0|          0
default_GameManager.getGameById                      |      1.33|100.00%|            80|            80|             0|             0|        0.0|          0
default_GameManager.getGameUrlByUrlKey               |      7.33|100.00%|           440|           440|             0|             0|        0.0|          0
default_InstanceManager.getInstanceById              |     30.98| 99.52%|         1,859|         1,850|             0|             0|        0.0|          0
default_InstanceManager.getInstanceById_local        |     30.98| 96.40%|         1,859|         1,792|             0|            67|        0.0|          0
default_InstanceManager.getInstanceById_remote       |      1.12| 86.57%|            67|            58|             0|             6|        0.0|          0
default_IssueDao.getIssueById                        |      7.62| 81.40%|           457|           372|             0|            63|        0.0|          0
default_IssueDao.getRecentOnSaleIssues               |      8.00| 85.21%|           480|           409|             0|            71|        0.0|          0
default_IssueDao.getRecentOpenAwardIssues            |      2.52| 82.78%|           151|           125|             0|            26|        0.0|          0
default_PrizeManager.getPrizeMap                     |      0.82|100.00%|            49|            49|             0|             0|        0.0|          0
default_TopicManager.getOnSaleTopics                 |      0.97|100.00%|            58|            58|             0|             0|        0.0|          0
default_TopicManager.getOnSaleTopics_local           |      0.97| 91.38%|            58|            53|             0|             5|        0.0|          0
default_TopicManager.getOnSaleTopics_remote          |      0.08|100.00%|             5|             5|             0|             0|        0.0|          0
default_TopicManager.getTopicByTopicId               |      2.90| 98.85%|           174|           172|             0|             0|        0.0|          0
default_TopicManager.getTopicByTopicId_local         |      2.90| 96.55%|           174|           168|             0|             6|        0.0|          0
default_TopicManager.getTopicByTopicId_remote        |      0.10| 66.67%|             6|             4|             0|             2|        0.0|          0
default_TopicManager.getTopicList                    |      0.02|100.00%|             1|             1|             0|             0|        0.0|          0
default_TopicManager.getTopicList_local              |      0.02|  0.00%|             1|             0|             0|             1|        0.0|          0
default_TopicManager.getTopicList_remote             |      0.02|100.00%|             1|             1|             0|             0|        0.0|          0
-----------------------------------------------------+----------+-------+--------------+--------------+--------------+--------------+-----------+-----------      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

隻有使用computeIfAbsent方法或者@Cached注解才會統計loadTime。用get方法取緩存,沒有命中的話自己去資料庫load,顯然是無法統計到的。

如果需要定制輸出,可以這樣做:

@Bean
    public SpringConfigProvider springConfigProvider() {
        return new SpringConfigProvider(){
            public Consumer<StatInfo> statCallback() {
                // return new StatInfoLogger(false);
                ... // 實作自己的logger
            }
        };
    }      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

JetCache按statIntervalMinutes指定的周期,定期調用statCallback傳回着這個Consumer,傳入的StatInfo是已經統計好的資料。這個方法預設的實作是:

returnnew StatInfoLogger(false);      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

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>      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache

JetCache2版本的@Cached和@CreateCache等注解都是基于Spring4.X版本實作的,在沒有Spring支援的情況下,注解将不能使用。但是可以直接使用JetCache的API來建立、管理、監控Cache,多級緩存也可以使用。

建立緩存

建立緩存的操作類似guava/caffeinecache,例如下面的代碼建立基于記憶體的LinkedHashMapCache:

Cache<String, Integer> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                .limit(100)
                .expireAfterWrite(200, TimeUnit.SECONDS)
                .buildCache();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

建立RedisCache:

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();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

多級緩存

在2.2以後通過下面的方式建立多級緩存:

Cache multiLevelCache = MultiLevelCacheBuilder.createMultiLevelCacheBuilder()
      .addCache(memoryCache, redisCache)
      .expireAfterWrite(100, TimeUnit.SECONDS)
      .buildCache();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

實際上,使用MultiLevelCache可以建立多級緩存,它的構造函數接收的是一個Cache數組(可變參數)。

如果是2.2之前的版本:

Cache memoryCache = ...
Cache redisCache = ...
Cache multiLevelCache = new MultiLevelCache(memoryCache, redisCache);      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

監控統計

如果要對Cache進行監控統計:

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();      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

首先建立一個CacheMonitor,每個DefaultCacheMonitor隻能用于一個Cache。當DefaultCacheMonitorManager啟動以後,會使用slf4j按指定的時間定期輸出統計資訊到日志中(簡版輸出格式參見

),DefaultCacheMonitor構造時指定的名字會作為輸出時cache的名字。

在組裝多級緩存的過程中,可以給每個緩存安裝一個Monitor,這樣可以監控每一級的命中情況。

也可以自己對統計資訊進行處理,調用下面的構造方法建立DefaultCacheMonitorManager:

public DefaultCacheMonitorManager(int resetTime, TimeUnit resetTimeUnit, Consumer<StatInfo> stat      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

clone下來以後,可以按maven項目導入idea或eclipse。

跑通單元測試,需要在本地運作redis,先安裝docker,然後用下面的指令運作redis-sentinel

docker run --rm -it -p 6379-6381:6379-6381 -p 26379-26381:26379-26381 areyouok/redis-sentinel      

接下來mvn cleantest可以跑通所有測試,如果在IDE裡面,可能還需要給javac設定-parameters參數。需要注意的是機器繁忙時單元測試有可能會失敗,因為很多單元測試使用了sleep,為了不讓單元測試運作的時間過長,sleep的時間都設定的比較短,這樣機器卡頓時可能導緻檢查失敗,不過對于正常機器這并不經常發生。

使用snapshot版本,在自己的pom裡面加上:

<repositories>
        <repository>
            <id>sonatype-nexus-snapshots</id>
            <name>Sonatype Nexus Snapshots</name>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>      
jetcache教程jetcache快速入門基本Cache API通過@CreateCache注解建立Cache執行個體通過注解實作方法緩存配置詳解進階Cache APIRedis支援(兩種redis用戶端二選一即可)記憶體緩存LinkedHashMapCache和CaffeineCache統計Builder:未使用Spring4(或者spring)的時候,通過Builder手工構造Cache開發者文檔更新和相容性指南

2.5.0

  • 從2.3.3及更低版本更新到2.5.0會發生ClassCastException(如果你使用了MultiLevelCache或者cacheType.CacheType.BOTH)。 解決辦法是先更新到2.4.4并且釋出到生産環境,然後再更新到2.5.0。
  • 子類的注解會覆寫接口和父類。