jetcache教程

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
日志服务 SLS,月写入数据量 50GB 1个月
简介: jetcache教程

jetcache

简介

JetCache是一个基于Java的缓存系统封装,提供统一的API和注解来简化缓存的使用。 JetCache提供了比SpringCache更加强大的注解,可以原生的支持TTL、两级缓存、分布式自动刷新,还提供了Cache接口用于手工缓存操作。当前有四个实现,RedisCacheTairCache(此部分未在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.8Spring 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。两个内存中的缓存实现LinkedHashMapCacheCaffeineCache也由它提供。
            • 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;

            image.gif

            用起来就像map一样

            UserDO user = userCache.get(123L);
            userCache.put(123L, user);
            userCache.remove(123L);

            image.gif

            创建一个两级(内存+远程)的缓存,内存中的元素个数限制在50个。

            @CreateCache(name = "UserService.userCache", expire = 100, cacheType = CacheType.BOTH, localLimit = 50)
            private Cache<Long, UserDO> userCache;

            image.gif

            name属性不是必须的,但是起个名字是个好习惯,展示统计数据的使用,会使用这个名字。如果同一个area两个@CreateCachename配置一样,它们生成的Cache将指向同一个实例。

            创建方法缓存

            使用@Cached方法可以为一个方法添加上缓存。JetCache通过Spring AOP生成代理,来支持缓存功能。注解可以加在接口方法上也可以加在类方法上,但需要保证是个Springbean

            public interface UserService {
                @Cached(name="UserService.getUserById", expire = 3600)
                User getUserById(long userId);
            }

            image.gif

            基本配置(使用Spring Boot)

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

            POM

            <dependency>
                <groupId>com.alicp.jetcache</groupId>
                <artifactId>jetcache-starter-redis</artifactId>
                <version>2.4.4</version>
            </dependency>

            image.gif

            配置一个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

            image.gif

            然后创建一个App类放在业务包的根下,EnableMethodCacheEnableCreateCacheAnnotation这两个注解分别激活CachedCreateCache注解,其他和标准的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);
                }
            }

            image.gif

            未使用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>

            image.gif

            配置了这个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;
                }
            }

            image.gif

            进一步阅读

              • CreateCache的详细使用说明可以看这里
              • 使用@CacheCache创建的Cache接口实例,它的API使用可以看这里
              • 关于方法缓存(@Cached, @CacheUpdate, @CacheInvalidate)的详细使用看这里
              • 详细的配置说明看这里

              基本Cache API

              简介

              JetCache2.0的核心是com.alicp.jetcache.Cache接口(以下简写为Cache),它提供了部分类似于javax.cache.CacheJSR107)的API操作。没有完整实现JSR107的原因包括:

                1. 希望维持API的简单易用。
                2. 对于特定的远程缓存系统来说,javax.cache.Cache中定义的有些操作无法高效率的实现,比如一些原子操作方法和类似removeAll()这样的方法。
                3. JSR107比较复杂,完整实现要做的工作很多。

                JSR107 style API

                以下是Cache接口中和JSR107javax.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);

                image.gif

                JetCache特有API

                V computeIfAbsent(K key, Function<K, V> loader)

                image.gif

                key对应的缓存不存在时,使用loader加载。通过这种方式,loader的加载时间可以被统计到。

                V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull)

                image.gif

                key对应的缓存不存在时,使用loader加载。cacheNullWhenLoaderReturnNull参数指定了当loader加载出来时null值的时候,是否要进行缓存(有时候即使是null值也是通过很繁重的查询才得到的,需要缓存)。

                V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expire, TimeUnit timeUnit)

                image.gif

                key对应的缓存不存在时,使用loader加载。cacheNullWhenLoaderReturnNull参数指定了当loader加载出来时null值的时候,是否要进行缓存(有时候即使是null值也是通过很繁重的查询才得到的,需要缓存)。expiretimeUnit指定了缓存的超时时间,会覆盖缓存的默认超时时间。

                void put(K key, V value, long expire, TimeUnit timeUnit)

                image.gif

                put操作,expiretimeUnit指定了缓存的超时时间,会覆盖缓存的默认超时时间。

                AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit)

                image.gif

                boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action)

                image.gif

                非堵塞的尝试获取一个锁,如果对应的key还没有锁,返回一个AutoReleaseLock,否则立即返回空。如果Cache实例是本地的,它是一个本地锁,在本JVM中有效;如果是redis等远程缓存,它是一个不十分严格的分布式锁。锁的超时时间由expiretimeUnit指定。多级缓存的情况会使用最后一级做tryLock操作。用法如下:

                // 使用try-with-resource方式,可以自动释放锁
                  try(AutoReleaseLock lock = cache.tryLock("MyKey",100, TimeUnit.SECONDS)){
                     if(lock != null){
                        // do something
                     }
                  }

                image.gif

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

                boolean hasRun = tryLockAndRun("MyKey",100, TimeUnit.SECONDS), () -> {
                    // do something
                  };

                image.gif

                tryLock内部会在访问远程缓存失败时重试,会自动释放,而且不会释放不属于自己的锁,比你自己做这些要简单。当然,基于远程缓存实现的任何分布式锁都不会是严格的分布式锁,不能和基于ZooKeeperConsul做的锁相比。

                大写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);

                image.gif

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

                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);
                }

                image.gif

                通过@CreateCache注解创建Cache实例

                简介

                Springbean中使用@CreateCache注解创建一个Cache实例。例如

                @CreateCache(expire = 100)
                private Cache<Long, UserDO> userCache;

                image.gif

                @CreateCache属性表

                属性

                默认值

                说明

                area

                “default”

                如果需要连接多个缓存系统,可在配置多个cache area,这个属性指定要使用的那个areaname

                name

                未定义

                指定缓存的名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。如果两个@CreateCachenamearea相同,它们会指向同一个Cache实例

                expire

                未定义

                Cache实例的默认超时时间定义,注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取无穷大

                timeUnit

                TimeUnit.SECONDS

                指定expire的单位

                cacheType

                CacheType.REMOTE

                缓存的类型,包括CacheType.REMOTECacheType.LOCALCacheType.BOTH。如果定义为BOTH,会使用LOCALREMOTE组合成两级缓存

                localLimit

                未定义

                如果cacheTypeCacheType.LOCALCacheType.BOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取100

                serialPolicy

                未定义

                如果cacheTypeCacheType.REMOTECacheType.BOTH,指定远程缓存的序列化方式。JetCache内置的可选值为SerialPolicy.JAVASerialPolicy.KRYO。注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取SerialPolicy.JAVA

                keyConvertor

                未定义

                指定KEY的转换方式,用于将复杂的KEY类型转换为缓存实现可以接受的类型,JetCache内置的可选值为KeyConvertor.FASTJSONKeyConvertor.NONENONE表示不转换,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);
                }

                image.gif

                key使用SpringSpEL脚本来指定。如果要使用参数名(比如这里的key="#userId"),项目编译设置target必须为1.8格式,并且指定javac-parameters参数,否则就要使用key="args[0]"这样按下标访问的形式。

                @CacheUpdate@CacheInvalidatenamearea属性必须和@Cached相同,name属性还会用做cachekey前缀。

                @Cached注解和@CreateCache的属性非常类似,但是多几个:

                属性

                默认值

                说明

                area

                “default”

                如果在配置中配置了多个缓存area,在这里指定使用哪个area

                name

                未定义

                指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。

                key

                未定义

                使用SpEL指定key,如果没有指定会根据所有参数自动生成。

                expire

                未定义

                超时时间。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为无穷大

                timeUnit

                TimeUnit.SECONDS

                指定expire的单位

                cacheType

                CacheType.REMOTE

                缓存的类型,包括CacheType.REMOTECacheType.LOCALCacheType.BOTH。如果定义为BOTH,会使用LOCALREMOTE组合成两级缓存

                localLimit

                未定义

                如果cacheTypeLOCALBOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为100

                serialPolicy

                未定义

                指定远程缓存的序列化方式。可选值为SerialPolicy.JAVASerialPolicy.KRYO。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为SerialPolicy.JAVA

                keyConvertor

                未定义

                指定KEY的转换方式,用于将复杂的KEY类型转换为缓存实现可以接受的类型,当前支持KeyConvertor.FASTJSONKeyConvertor.NONENONE表示不转换,FASTJSON可以将复杂对象KEY转换成String。如果注解上没有定义,会使用全局配置。

                enabled

                true

                是否激活缓存。例如某个dao方法上加缓存注解,由于某些调用场景下不能有缓存,所以可以设置enabledfalse,正常调用不会使用缓存,在需要的地方可使用CacheContext.enableCache在回调中激活缓存,缓存激活的标记在ThreadLocal上,该标记被设置后,所有enable=false的缓存都被激活

                cacheNullValue

                false

                当方法返回值为null的时候是否要缓存

                condition

                未定义

                使用SpEL指定条件,如果表达式返回true的时候才进行缓存

                @CacheInvalidate注解说明:

                属性

                默认值

                说明

                area

                “default”

                如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。

                name

                未定义

                指定缓存的唯一名称,指向对应的@Cached定义。

                key

                未定义

                使用SpEL指定key

                condition

                未定义

                使用SpEL指定条件,如果表达式返回true才执行删除

                @CacheUpdate注解说明:

                属性

                默认值

                说明

                area

                “default”

                如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。

                name

                未定义

                指定缓存的唯一名称,指向对应的@Cached定义。

                key

                未定义

                使用SpEL指定key

                value

                未定义

                使用SpEL指定value

                condition

                未定义

                使用SpEL指定条件,如果表达式返回true才执行更新

                使用@CacheUpdate@CacheInvalidate的时候,相关的缓存操作可能会失败(比如网络IO错误),所以指定缓存的超时时间是非常重要的。

                @CacheRefresh注解说明:

                属性

                默认值

                说明

                refresh

                未定义

                刷新间隔

                timeUnit

                TimeUnit.SECONDS

                时间单位

                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}

                image.gif

                配置通用说明如下

                属性

                默认值

                说明

                jetcache.statIntervalMinutes

                0

                统计间隔,0表示不统计

                jetcache.areaInCacheName

                true

                jetcache-annocacheName作为远程缓存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

                缓存类型。tairredis为当前支持的远程缓存;linkedhashmapcaffeine为当前支持的本地缓存类型

                jetcache.[local|remote].${area}.keyConvertor

                key转换器的全局配置,当前只有一个已经实现的keyConvertorfastjson。仅当使用@CreateCache且缓存类型为LOCAL时可以指定为none,此时通过equals方法来识别key。方法缓存必须指定keyConvertor

                jetcache.[local|remote].${area}.valueEncoder

                java

                序列化器的全局配置。仅remote类型的缓存需要指定,可选javakryo

                jetcache.[local|remote].${area}.valueDecoder

                java

                序列化器的全局配置。仅remote类型的缓存需要指定,可选javakryo

                jetcache.[local|remote].${area}.limit

                100

                每个缓存实例的最大元素的全局配置,仅local类型的缓存需要指定。注意是每个缓存实例的限制,而不是全部,比如这里指定100,然后用@CreateCache创建了两个缓存实例(并且注解上没有设置localLimit属性),那么每个缓存实例的限制都是100

                jetcache.[local|remote].${area}.expireAfterWriteInMillis

                无穷大

                以毫秒为单位指定超时时间的全局配置(以前为defaultExpireInMillis)

                jetcache.local.${area}.expireAfterAccessInMillis

                0

                需要jetcache2.2以上,以毫秒为单位,指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持。0表示不使用这个功能。

                上表中${area}对应@Cached@CreateCachearea属性。注意如果注解上没有指定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返回的结果都是异步的。当前支持异步的实现只有jetcacheredis-luttece实现,其他的缓存实现(内存中的、TairJedis等),所有的异步接口都会同步堵塞,这样API仍然是兼容的。

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

                  CacheGetResult<UserDO> r = cache.GET(userId);

                  image.gif

                  这一行代码执行完以后,缓存操作可能还没有完成,如果此时调用r.isSuccess()或者r.getValue()或者r.getMessage()将会堵塞直到缓存操作完成。如果不想被堵塞,并且需要在缓存操作完成以后执行后续操作,可以这样做:

                  CompletionStage<ResultData> future = r.future();
                  future.thenRun(() -> {
                      if(r.isSuccess()){
                          System.out.println(r.getValue());
                      }
                  });

                  image.gif

                  以上代码将会在缓存操作异步完成后,在完成异步操作的线程中调用thenRun中指定的回调。CompletionStageJava8新增的功能,如果对此不太熟悉可以先查阅相关的文档。需要注意的是,既然已经选择了异步的开发方式,在回调中不能调用堵塞方法,以免堵塞其他的线程(回调方法很可能是在event loop线程中执行的)。

                  部分小写的api不需要任何修改,就可以直接享受到异步开发的好处。比如putremoveAll方法,由于它们没有返回值,所以此时就直接优化成异步调用,能够减少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();

                  image.gif

                  LoadingCachegetgetAll方法,在缓存未命中的情况下,会调用loader,如果loader抛出一场,getgetAll会抛出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);
                    }

                    image.gif

                    @CreateCache总是初始化一个经过LoadingCache包装的Cache,直接在config中设置loader,可以实时生效。

                    自动刷新缓存

                    JetCache2.2版本开始,RefreshCache基于decorator模式提供了自动刷新的缓存的能力,目的是为了防止缓存失效时造成的雪崩效应打爆数据库。同时设置了loaderrefreshPolicy的时候,CacheBuilderbuildCache方法返回的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();

                    image.gif

                    对一些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);
                    }

                    image.gif

                    Redis支持(两种redis客户端二选一即可)

                    使用jedis客户端连接redis

                    redis有多种java版本的客户端,JetCache2.2以前使用jedis客户端访问redis。从JetCache2.2版本开始,增加了对luttece客户端的支持,jetcacheluttece支持提供了异步操作和redis集群支持。

                    如果选用jedis访问redis,对应的maven artifactjetcache-redisjetcache-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

                    image.gif

                     
                             

                    image.gif

                    如果需要直接操作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);
                    }

                    image.gif

                    然后可以直接使用

                    @Autowired
                    private Pool<Jedis> defaultPool;

                    image.gif

                    也可以用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;
                        }
                    }

                    image.gif

                    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();

                    image.gif

                    常见问题

                    如果遇到这个错误

                    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>

                    image.gif

                    使用lettuce客户端连接redis

                    redis有多种java版本的客户端,JetCache2.2以前使用jedis客户端访问redis。从JetCache2.2版本开始,增加了对lettuce客户端的支持,JetCachelettuce支持提供了异步操作和redis集群支持。

                    使用lettuce访问redis,对应的maven artifactjetcache-redis-lettucejetcache-starter-redis-lettucelettuce使用Netty建立单个连接连redis,所以不需要配置连接池。

                    注意:新发布的lettuce5更换了groupId和包名,2.3版本的JetCache同时支持lettuce45jetcache-redis-lettucejetcache-starter-redis-lettuce提供lettuce5支持,jetcache-redis-lettuce4jetcache-starter-redis-lettuce4提供lettuce4支持。

                    注意:JetCache2.2版本中,lettuce单词存在错误的拼写,错写为“luttece”,该错误存在于包名、类名和配置中,2.3已经改正。

                    spring boot环境下的lettuce支持

                    application.yml文件如下(这里省去了local相关的配置):

                    jetcache: 
                      areaInCacheName: false
                      remote:
                        default:
                          type: redis.lettuce
                          keyConvertor: fastjson
                          uri: redis://127.0.0.1:6379/

                    image.gif

                     
                             

                    image.gif

                    如果使用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

                    image.gif

                     
                             

                    image.gif

                    如果需要直接使用lettuceRedisClient

                    @Bean(name = "defaultClient")
                    @DependsOn(RedisLettuceAutoConfiguration.AUTO_INIT_BEAN_NAME)
                    public LettuceFactory defaultClient() {
                        return new LettuceFactory("remote.default", RedisClient.class);
                    }

                    image.gif

                    然后可以直接使用

                    @Autowired
                    private RedisClient defaultClient;

                    image.gif

                    也可以用Cache接口上的<T> Tunwrap(Class<T> clazz)方法来获取RedisClientRedisCommands等。参考RedisLettuceCache.unwrap源代码。

                    不使用spring boot

                    @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;
                        }
                    }

                    image.gif

                    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();

                    image.gif

                    内存缓存LinkedHashMapCache和CaffeineCache

                    本地缓存当前有两个实现。如果自己用jetcache-coreCache API,可以不指定keyConvertor,此时本地缓存使用equals方法来比较key如果使用jetcache-anno中的@Cached@CreateCache等注解,必须指定keyConvertor

                    LinkedHashMapCache

                    LinkedHashMapCacheJetCache中实现的一个最简单的Cache,使用LinkedHashMapLRU方式淘汰。

                    Cache<Long, OrderDO> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                                    .limit(100)
                                    .expireAfterWrite(200, TimeUnit.SECONDS)
                                    .buildCache();

                    image.gif

                    CaffeineCache

                    caffeine cache的介绍看这里,它是guava cache的后续作品。

                    Cache<Long, OrderDO> cache = CaffeineCacheBuilder.createCaffeineCacheBuilder()
                                    .limit(100)
                                    .expireAfterWrite(200, TimeUnit.SECONDS)
                                    .buildCache();

                    image.gif

                    统计

                    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
                    -----------------------------------------------------+----------+-------+--------------+--------------+--------------+--------------+-----------+-----------

                    image.gif

                    只有使用computeIfAbsent方法或者@Cached注解才会统计loadTime。用get方法取缓存,没有命中的话自己去数据库load,显然是无法统计到的。

                    如果需要定制输出,可以这样做:

                    @Bean
                        public SpringConfigProvider springConfigProvider() {
                            return new SpringConfigProvider(){
                                public Consumer<StatInfo> statCallback() {
                                    // return new StatInfoLogger(false);
                                    ... // 实现自己的logger
                                }
                            };
                        }

                    image.gif

                    JetCachestatIntervalMinutes指定的周期,定期调用statCallback返回着这个Consumer,传入的StatInfo是已经统计好的数据。这个方法默认的实现是:

                    returnnew StatInfoLogger(false);

                    image.gif

                    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>

                    image.gif

                    Builder:未使用Spring4(或者spring)的时候,通过Builder手工构造Cache

                    JetCache2版本的@Cached@CreateCache等注解都是基于Spring4.X版本实现的,在没有Spring支持的情况下,注解将不能使用。但是可以直接使用JetCacheAPI来创建、管理、监控Cache,多级缓存也可以使用。

                    创建缓存

                    创建缓存的操作类似guava/caffeinecache,例如下面的代码创建基于内存的LinkedHashMapCache

                    Cache<String, Integer> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
                                    .limit(100)
                                    .expireAfterWrite(200, TimeUnit.SECONDS)
                                    .buildCache();

                    image.gif

                    创建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();

                    image.gif

                    多级缓存

                    2.2以后通过下面的方式创建多级缓存:

                    Cache multiLevelCache = MultiLevelCacheBuilder.createMultiLevelCacheBuilder()
                          .addCache(memoryCache, redisCache)
                          .expireAfterWrite(100, TimeUnit.SECONDS)
                          .buildCache();

                    image.gif

                    实际上,使用MultiLevelCache可以创建多级缓存,它的构造函数接收的是一个Cache数组(可变参数)。

                    如果是2.2之前的版本:

                    Cache memoryCache = ...
                    Cache redisCache = ...
                    Cache multiLevelCache = new MultiLevelCache(memoryCache, redisCache);

                    image.gif

                    监控统计

                    如果要对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();

                    image.gif

                    首先创建一个CacheMonitor,每个DefaultCacheMonitor只能用于一个Cache。当DefaultCacheMonitorManager启动以后,会使用slf4j按指定的时间定期输出统计信息到日志中(简版输出格式参见统计),DefaultCacheMonitor构造时指定的名字会作为输出时cache的名字。

                    在组装多级缓存的过程中,可以给每个缓存安装一个Monitor,这样可以监控每一级的命中情况。

                    也可以自己对统计信息进行处理,调用下面的构造方法创建DefaultCacheMonitorManager

                    public DefaultCacheMonitorManager(int resetTime, TimeUnit resetTimeUnit, Consumer<StatInfo> stat

                    image.gif

                    开发者文档

                    clone下来以后,可以按maven项目导入ideaeclipse

                    跑通单元测试,需要在本地运行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>

                    image.gif

                    升级和兼容性指南

                    2.5.0

                      • 从2.3.3及更低版本升级到2.5.0会发生ClassCastException(如果你使用了MultiLevelCache或者cacheType.CacheType.BOTH)。 解决办法是先升级到2.4.4并且发布到生产环境,然后再升级到2.5.0。
                      • 子类的注解会覆盖接口和父类。
                      相关实践学习
                      基于Redis实现在线游戏积分排行榜
                      本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
                      云数据库 Redis 版使用教程
                      云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
                      相关文章
                      |
                      缓存 NoSQL Java
                      【JetCache】JetCache的使用方法与步骤
                      【JetCache】JetCache的使用方法与步骤
                      3169 0
                      |
                      缓存 Java Redis
                      JetCache快速入门
                      本文简要介绍了基于注解来配置使用JetCache的方式。当使用JetCache的注解时,可以搭配Spring4.0.8以上版本,或者Spring Boot 1.1.9以上版本,本文同时展示了这两种情况下的配置方式。
                      18578 0
                      QGS
                      |
                      6月前
                      |
                      前端开发 Java easyexcel
                      Springboot3+EasyExcel由浅入深
                      Springboot3+EasyExcel由浅入深
                      QGS
                      283 1
                      |
                      6月前
                      |
                      安全 Java 数据库
                      SpringBoot - 安全入门与SpringSecurity
                      SpringBoot - 安全入门与SpringSecurity
                      85 0
                      |
                      缓存 Java Spring
                      缓存 - Spring Boot 整合 Caffeine 不完全指北
                      缓存 - Spring Boot 整合 Caffeine 不完全指北
                      395 0
                      |
                      存储 缓存 安全
                      Shiro入门学习(整合SpringBoot)
                      Apache Shiro是Java的一个安全(权限)框架 可以完成:认证、授权、加密、会话管理、与Web集成和缓存等 用户登录时把身份信息(用户名/手机号/邮箱地址等)和凭证信息(密码/证书等)封装成一个Token令牌,通过安全管理器中的认证器进行校验,成功则授权以访问系统
                      |
                      Java 数据库连接 数据库
                      基于SpringBoot的MybatisPlus简明教程(1)
                      基于SpringBoot的MybatisPlus简明教程(1)
                      79 0
                      |
                      Java 数据库
                      基于SpringBoot的MybatisPlus简明教程(2)
                      基于SpringBoot的MybatisPlus简明教程(2)
                      51 0
                      |
                      Java 数据库
                      Springboot整合mybatisplus快速入门
                      Springboot整合mybatisplus快速入门
                      Springboot整合mybatisplus快速入门
                      |
                      NoSQL Java Redis
                      【笑小枫的SpringBoot系列】【八】SpringBoot集成Redis
                      【笑小枫的SpringBoot系列】【八】SpringBoot集成Redis
                      161 0