学习之-JetCacheJava缓存框架最全详解

目录

jetcache官方源码:

一、JetCache介绍

1.1 诞生

1.2 整合-开源界大放异彩

1.3 挑战-SpringCache江湖地位

1.4 JetCache核心特性和优势

2.1 使用要求

2.2 项目代码介绍

二、JetCache核心注解

@Cached注解说明

@CacheRefresh注解说明:

@CacheInvalidate注解说明:

@CacheUpdate注解说明:

@CachePenetrationProtect注解:

三、JetCache接入配置

1、依赖哪个jar?

2、官方-Springboot 项目配置

3、官方-未使用Springboot 的项目配置

本地缓存-LinkedHashMapCache

本地缓存-caffeine

统计

官网信息

官方配置

logback日志输出配置

4、适配公司sedis客户端-未使用Springboot 的项目配置

指标的解读


jetcache官方源码:

github:GitHub - alibaba/jetcache: JetCache is a Java cache framework.

官方文档:Home · alibaba/jetcache Wiki · GitHub

一、JetCache介绍

1.1 诞生

2013年

JetCache诞生于 [ 阿里彩票 ],作者是 [ huangli ] 凭借得天独厚的Tair支持和丰富的Spring生态注解支持,赢得了大家的喜爱。
2015年随着SpringBoot的大热和集团内PandoraBoot的彻底铺开,JetCache以Starter的形式实现了扩展,优化了配置项,在架构设计和性能上更上一层楼。
2015年同年JetCache开源至Github,作为alibaba的开源缓存框架,其易用性和设计先进性吸引了大批国内外用户,截止当前在github上累计3.7k star,870 fork。
2018年JetCache最大版本更新,对整体的设计进行了调整,修改了默认的序列化方式,集成支持了SpringData,RedisLettuce,Redisson等更加高效以及功能更加灵活且高级的三方SDK。

1.2 整合-开源界大放异彩

支持范围方面JetCache原生支持的远程缓存是Tair,但是Tair在集团外并不可用。JetCache为了拥抱开源,实现了时下主流的GuavaCache, CaffeineCache,  Redis,MemCache基本覆盖了国内的主流缓存中间件
在功能性方面JetCache满足了用户一行注解解决Method缓存的刚需,同时也能通过叠加注解的方式非常高效的处理缓存穿透,缓存击穿,缓存雪崩,缓存失效等经典分布式缓存的问题,这让用户充分体验到了缓存框架的效率优势和设计先进性。
在扩展性方面JetCache满足了用户一行注解解决Method缓存的刚需,也提供了优秀的扩展能力。想要实现一个新的Cache类型,只需要实现AbstractEmbeddedCache或者AbstractExternalCache就可以以非常低廉的成本实现一个新的缓存框架。

1.3 挑战-SpringCache江湖地位

对比SpringCacheJetCache
出身

在2015年最火的框架是SpringBoot,SpringBoot提供了非常丰富的组件支持以及模块化的组件管理,其中就包括基于JSR-107–JCacheAPI实现的SpringCache框架。

JetCache是阿里推出的一套替代springcache的缓存方案。JetCache是对SpringCache进行了封装。在阿里实际业务应用实战后开源的缓存框架
依赖性SpringCache 是 Spring 框架自带的缓存框架JetCache 是独立的缓存框架,不依赖于任何框架。
使用方式SpringCache 集成于 Spring 框架中,使用起来比较简单,只需要在代码中添加注解即可JetCache 需要手动引入依赖并配置相关参数,使用起来相对复杂一些
分布式

SpringCache框架很好的实现了JCacheAPI,在当时占据了非常有力的位置,几乎所有的SpringBoot初创项目,都选择了使用SpringCache来作为他们的第一个缓存框架。但随着软件工程的规模越来越大,分布式场景的经典问题也接踵而至,显然SpringCache在应对分布式环境的经典问题时显得太过于稚嫩。

对于分布式场景, 分布式锁,缓存穿透,缓存击穿,缓存雪崩 等经典问题,缺少足够成熟的方案。

在SpringCache基础上实现了多级缓存、缓存统计、自动刷新(防止某个缓存失效,突然访问量增大,导致数据库挂掉的缓存雪崩)、异步调用、数据报表等功能。

支持分布式锁,JetCache提供了分布式锁和分布式计数器等功能,以支持分布式环境下的并发控制。

缓存层级仅支持一级缓存,与提供了基本的缓存功能

支持多级缓存滑动窗口,缓存序列化,异步API支持等实际工作场景经常会需要用到的核心能力

支持多种缓存策略,包括基于时间的过期策略、基于LRU算法的自动清理策略、基于手动清理的策略等。此外,

扩展性方面对于扩展性上,设计的不够开放和正交,很难低成本的完成一些高级功能的扩展。在迁移缓存方面基本上可以做到换注解平替,所以一旦工程规模达到一定量级,很多架构师会选择从SpringCache的方式切换到JetCache上。
总体来说,如果需要更高级的缓存特性,或者需要更好的性能表现,可以选择 JetCache。如果只需要基本的缓存功能,或者已经使用了 Spring 框架,可以选择 SpringCache。

1.4 JetCache核心特性和优势

etCache设定了本地缓存与远程缓存的多级缓存方案:

提供统一的,类似jsr-107风格的API访问Cache,并可通过注解创建并配置Cache实例

支持多种缓存类型JetCache支持多种缓存类型,包括本地缓存、远程缓存和分布式缓存,可以根据业务需求选择合适的缓存类型。
支持多种缓存协议JetCache支持多种缓存协议,包括Redis、Memcached、Couchbase等,可以与多种缓存系统进行集成。可选择内存缓存、分布式缓存,或者同时存在,同时存在时优先访问内存
支持多种缓存策略JetCache支持多种缓存策略,包括FIFO、LRU、LFU等,可以根据业务需求选择合适的缓存策略。
支持注解和编程式缓存JetCache支持注解和编程式缓存,可以根据业务需求选择合适的缓存方式。
支持分布式事务JetCache支持分布式事务,可以保证缓存和数据库的一致性。分布式缓存自动刷新,分布式锁 (2.2+)
支持缓存监控和统计JetCache支持缓存监控和统计,可以实时查看缓存的使用情况和性能指标
易于使用和扩展JetCache具有简单易用的API和灵活的扩展机制,可以方便地集成到各种应用中。Key的生成策略和Value的序列化策略是可以定制的、支持异步Cache API

2.1 使用要求

JetCache需要JDK1.8、Spring Framework4.0.8以上版本。Spring Boot为可选,需要1.1.9以上版本。如果不使用注解(仅使用jetcache-core),Spring Framework也是可选的,此时使用方式与Guava/Caffeine cache类似。

2.2 项目代码介绍

JSR-107--缓存JCache标准抽象实

Java在2012的JSR-107协议中新增了关于缓存的抽象设计标准–JCache。

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

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

丰富注解-无侵入抽象设计


二、JetCache核心注解

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

@Cached注解说明

  1. 在方法上添加 @Cached 注解,指定缓存的 key 和过期时间等参数。

  2. 当方法被调用时,JetCache 会先从缓存中查找对应的数据。

  3. 如果缓存中存在数据,则直接返回缓存中的数据,不再执行方法体。

  4. 如果缓存中不存在数据,则执行方法体,并将方法的返回值存入缓存中。

  5. 后续调用该方法时,将直接从缓存中获取数据,不再执行方法体。

  6. 缓存的过期时间到达后,缓存数据将被自动清除,下次调用该方法将重新执行方法体并更新缓存数据。

JetCache 支持多种缓存类型,包括本地内存缓存、Redis 缓存、Caffeine 缓存等,可以根据实际需求进行配置。同时,JetCache 还提供了缓存预热、缓存穿透、缓存雪崩等解决方案,以提高缓存的效率和稳定性。

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

属性默认值备注
area“default”如果在配置中配置了多个缓存area,在这里指定使用哪个area
name未定义指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。
key未定义使用SpEL指定key,如果没有指定会根据所有参数自动生成。
expire未定义超时时间。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为无穷大
timeUnitTimeUnit.SECONDS指定expire的单位
cacheTypeCacheType.REMOTE缓存的类型,包括CacheType.REMOTE、CacheType.LOCAL、CacheType.BOTH。如果定义为BOTH,会使用LOCAL和REMOTE组合成两级缓存
localLimit未定义

如果cacheType为LOCAL或BOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为100,参数 localLimit 表示本地缓存的最大条目限制。当使用 JetCache 的本地缓存功能时,localLimit 参数可以控制本地缓存中最多缓存的条目数量,如果超过了这个数量,将会按照 LRU(Least Recently Used)算法删除最近最少使用的缓存条目,以保持缓存的大小在限制范围内。localLimit 默认值为 100条,可以通过设置该值来优化缓存性能和内存使用。

备注:当type为LOCAL时,配置localLimit,当key值超出限制条件,会删除最少使用的条目,重新请求缓存过的,会走到db再缓存一次。

当type为BOTH时,虽然配置了localLimit 当key值超出限制条件,本地会删除使用最少得条目,但是远程还有缓存,查询缓存时是优先查询本地,没有再去远程,都没有再走db,将查询结果进行一次缓存。如果配置BOTH时,可以配置该项减少本地内存使用,但是不影响缓存整体使用因为有远程兜底。

localExpire未定义仅当cacheType为BOTH时适用,为内存中的Cache指定一个不一样的超时时间,通常应该小于expire
serialPolicy未定义指定远程缓存的序列化方式。可选值为SerialPolicy.JAVA和SerialPolicy.KRYO。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为SerialPolicy.JAVA
keyConvertor未定义指定KEY的转换方式,用于将复杂的KEY类型转换为缓存实现可以接受的类型,当前支持KeyConvertor.FASTJSON和KeyConvertor.NONE。NONE表示不转换,FASTJSON可以将复杂对象KEY转换成String。如果注解上没有定义,会使用全局配置。
enabledtrue是否激活缓存。例如某个dao方法上加缓存注解,由于某些调用场景下不能有缓存,所以可以设置enabled为false,正常调用不会使用缓存,在需要的地方可使用CacheContext.enableCache在回调中激活缓存,缓存激活的标记在ThreadLocal上,该标记被设置后,所有enable=false的缓存都被激活
cacheNullValuefalse当方法返回值为null的时候是否要缓存
condition未定义使用SpEL指定条件,如果表达式返回true的时候才去缓存中查询
postCondition未定义使用SpEL指定条件,如果表达式返回true的时候才更新缓存,该评估在方法执行后进行,因此可以访问到#result

@CacheRefresh注解说明:

(强制刷新即使缓存结果未到时效时间)

属性

默认值

说明

refresh未定义刷新间隔
timeUnitTimeUnit.SECONDS时间单位
stopRefreshAfterLastAccess未定义指定该key多长时间没有访问就停止刷新,如果不指定会一直刷新
refreshLockTimeout60秒类型为BOTH/REMOTE的缓存刷新时,同时只会有一台服务器在刷新,这台服务器会在远程缓存放置一个分布式锁,此配置指定该锁的超时时间

@CacheInvalidate注解说明:

属性

默认值

说明

area“default”如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。
name未定义指定缓存的唯一名称,指向对应的@Cached定义。
key未定义使用SpEL指定key
condition未定义使用SpEL指定条件,如果表达式返回true才执行删除,可访问方法结果#result

@CacheUpdate注解说明:

属性

默认值

说明

area“default”如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。
name未定义指定缓存的唯一名称,指向对应的@Cached定义。
key未定义使用SpEL指定key
value未定义使用SpEL指定value
condition未定义

使用SpEL指定条件,如果表达式返回true才执行更新,可访问方法结果#result

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

@CachePenetrationProtect注解:

当缓存访问未命中的情况下,对并发进行的加载行为进行保护。 当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。

对于以上未定义默认值的参数,如果没有指定,将使用yml中指定的全局配置,全局配置请参考配置说明

三、JetCache接入配置

1、依赖哪个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。

项目需要使用二级缓存(远程使用redis),用注解加到接口的方式实现,选用jetcache-anno-api、jetcache-redis 两个jar包

2、官方-Springboot 项目配置

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>${jetcache.latest.version}</version>
</dependency>

配置一个spring boot风格的application.yml文件,把他放到资源目录中

jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
  remote:
    default:
      type: redis
      keyConvertor: fastjson2
      broadcastChannel: projectA
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: 127.0.0.1
      port: 6379

然后创建一个App类放在业务包的根下,EnableMethodCache,EnableCreateCacheAnnotation这两个注解分别激活Cached和CreateCache注解,其他和标准的Spring Boot程序是一样的。这个类可以直接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);
    }
}

3、官方-未使用Springboot 的项目配置

如果没有使用spring boot,可以按下面的方式配置(这里使用jedis客户端连接redis为例)。

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-anno</artifactId>
    <version>${jetcache.latest.version}</version>
</dependency>

官方配置文档

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.Fastjson2KeyConvertor;
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 // deprecated in jetcache 2.7, 如果不用@CreateCache注解可以删除
@Import(JetCacheBaseBeans.class) //need since jetcache 2.7+
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 for jetcache <=2.6
    //public SpringConfigProvider springConfigProvider() {
    //    return new SpringConfigProvider();
    //}
 
    @Bean
    public GlobalCacheConfig config(Pool<Jedis> pool){
    // public GlobalCacheConfig config(SpringConfigProvider configProvider, Pool<Jedis> pool){ // for jetcache <=2.5
        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(Fastjson2KeyConvertor.INSTANCE)
                .valueEncoder(JavaValueEncoder.INSTANCE)
                .valueDecoder(JavaValueDecoder.INSTANCE)
                .broadcastChannel("projectA")
                .jedisPool(pool);
        remoteBuilders.put(CacheConsts.DEFAULT_AREA, remoteCacheBuilder);
 
        GlobalCacheConfig globalCacheConfig = new GlobalCacheConfig();
        // globalCacheConfig.setConfigProvider(configProvider); // for jetcache <= 2.5
        globalCacheConfig.setLocalCacheBuilders(localBuilders);
        globalCacheConfig.setRemoteCacheBuilders(remoteBuilders);
        globalCacheConfig.setStatIntervalMinutes(15);
        //globalCacheConfig.setAreaInCacheName(false); for jetcache <=2.6
 
        return globalCacheConfig;
    }
}

本地缓存-LinkedHashMapCache

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

LinkedHashMapCache是JetCache中实现的一个最简单的Cache,使用LinkedHashMap做LRU方式淘汰。

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

本地缓存-caffeine

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

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

4、统计

官网信息

jetcache/Stat.md at master · alibaba/jetcache · GitHub

当yml中的jetcache.statIntervalMinutes大于0时,通过@CreateCache和@Cached配置出来的Cache自带监控。JetCache会按指定的时间定期通过logger输出统计信息。默认输出信息类似如下:

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

官方配置

如果需要定制输出,可以这样做:,按下文配置会在hotdog.debug.log文件中展示统计图,如果要制定输出到某日志需要配置logback,配置信息下文有

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

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

return new StatInfoLogger(false);

StatInfoLogger的构造参数设置为true会有更详细的统计信息,包括put等操作的统计。StatInfoLogger输出的是给人读的信息,你也可以自定义logger将日志输出成特定格式,然后通过日志系统统一收集和统计。

自定义统计示例: jetCache会自动回调这个方法,回调周期按照setStatIntervalMinutes(2);配置的时间

logback日志输出配置

如果想要让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>

统计日志:

5、适配公司sedis客户端-未使用Springboot 的项目配置

@Configuration
@EnableMethodCache(basePackages = "应用范围包名路径")
@Import(JetCacheBaseBeans.class)
public class JetCacheConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(JetCacheConfig.class);

    protected int maxNameLength = 65;


    @Bean
    public Consumer<StatInfo> statCallback() {
        //自定义Logger,实现自定义埋点+统计日志打印
        return new JetCacheLogger();
    }

    @Bean
    public GlobalCacheConfig config(@Qualifier("jetCacheSedis") AdvancedSedis3 jetCacheSedis) {
        //这里参数的 jetCacheSedis 为本项目自己封装的redis,使用自己项目封装好的redis即可,
        //或者照着官网示例代码的redis pool 配置也可以       

        Map localBuilders = new HashMap();
        CaffeineCacheBuilder.CaffeineCacheBuilderImpl caffeineCacheBuilder = CaffeineCacheBuilder.createCaffeineCacheBuilder()
                .keyConvertor(Fastjson2KeyConvertor.INSTANCE);
        localBuilders.put(CacheConsts.DEFAULT_AREA, caffeineCacheBuilder);

        Map remoteBuilders = new HashMap();
        SedisJetCacheBuilder.SedisDataCacheBuilderImpl builder = SedisJetCacheBuilder.createBuilder()
                .keyConvertor(Fastjson2KeyConvertor.INSTANCE)
                .valueEncoder(Kryo5ValueEncoder.INSTANCE)
                .valueDecoder(Kryo5ValueDecoder.INSTANCE)
                .broadcastChannel("t_desert_feed")
                .keyPrefix("jetCache-")
                .sedisClient(jetCacheSedis);//放入redis

        remoteBuilders.put(CacheConsts.DEFAULT_AREA, builder);
        GlobalCacheConfig globalCacheConfig = new GlobalCacheConfig();
        globalCacheConfig.setRemoteCacheBuilders(remoteBuilders);
        globalCacheConfig.setLocalCacheBuilders(localBuilders);
        globalCacheConfig.setStatIntervalMinutes(1);//设置每次间隔多久统计
        return globalCacheConfig;
    }


}

自定义Logger处理埋点和统计日志打印

public class JetCacheLogger implements Consumer<StatInfo> {
    private static final String JETCACHE_MONITOR_PREFIX = "jetcache.moniter.";
    private static Logger logger = LoggerFactory.getLogger(StatInfoLogger.class);//此处logger类需要StatInfoLogger这个
 
    protected int maxNameLength = 65;
 
    @Override
    public void accept(StatInfo statInfo) {
 
        //埋点
        List<CacheStat> stats = statInfo.getStats();
        if (stats != null && stats.size() > 0) {
            for (CacheStat c : stats) {
                String monitorKey = JETCACHE_MONITOR_PREFIX + c.getCacheName();
                long get = c.getGetCount();
                long hit = c.getGetHitCount();
                long fail = c.getGetFailCount();
                long expire = c.getGetExpireCount();
                double avgLoadTime = c.avgLoadTime() * 100;
                double maxLoadTime = c.getMaxLoadTime() * 100;
                //此处为项目封装的埋点方法,需要替换为自己项目埋点监控方法
                Monitor.recordMany(monitorKey + ".get", get, 0);
                Monitor.recordMany(monitorKey + ".hit", hit, 0);
                Monitor.recordMany(monitorKey + ".fail", fail, 0);
                Monitor.recordMany(monitorKey + ".expire", expire, 0);
                Monitor.recordQuantile(monitorKey + ".avgLoadTime", (long) avgLoadTime);
                Monitor.recordQuantile(monitorKey + ".maxLoadTime", (long) maxLoadTime);
            }
        }
        //日志,此处为复制StatInfoLogger 中的方法
        Collections.sort(stats, (o1, o2) -> {
            if (o1.getCacheName() == null) {
                return -1;
            } else if (o2.getCacheName() == null) {
                return 1;
            } else {
                return o1.getCacheName().compareTo(o2.getCacheName());
            }
        });
        StringBuilder sb;
        sb = logStatSummary(statInfo);//只使用总结统计,明细统计使用logVerbose这个方法且需要复制logVerbose方法的实现
        logger.info(sb.toString());
    }
 
    private StringBuilder logTitle(int initSize, StatInfo statInfo) {
        StringBuilder sb = new StringBuilder(initSize);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
        sb.append("jetcache stat from ").append(sdf.format(new Date(statInfo.getStartTime())))
                .append(" to ").append(sdf.format(statInfo.getEndTime())).append("\n");
        return sb;
    }
 
    private void printSepLine(StringBuilder sb, String title) {
        title.chars().forEach((c) -> {
            if (c == '|') {
                sb.append('+');
            } else {
                sb.append('-');
            }
        });
        sb.append('\n');
    }
 
    private StringBuilder logStatSummary(StatInfo statInfo) {
        StringBuilder sb = logTitle(2048, statInfo);
 
        List<CacheStat> stats = statInfo.getStats();
        OptionalInt maxCacheNameLength = stats.stream().mapToInt((s) -> getName(s.getCacheName()).length()).max();
        int len = Math.max(5, maxCacheNameLength.orElse(0));
 
        String title = String.format("%-" + len + "s|%10s|%7s|%14s|%14s|%14s|%14s|%11s|%11s", "cache", "qps", "rate", "get", "hit", "fail", "expire", "avgLoadTime", "maxLoadTime");
        sb.append(title).append('\n');
        printSepLine(sb, title);
        for (CacheStat s : stats) {
            sb.append(String.format("%-" + len + "s", getName(s.getCacheName()))).append('|');
            sb.append(String.format("%,10.2f", s.qps())).append('|');
            sb.append(String.format("%6.2f%%", s.hitRate() * 100)).append('|');
            sb.append(String.format("%,14d", s.getGetCount())).append('|');
            sb.append(String.format("%,14d", s.getGetHitCount())).append('|');
            sb.append(String.format("%,14d", s.getGetFailCount())).append('|');
            sb.append(String.format("%,14d", s.getGetExpireCount())).append('|');
            sb.append(String.format("%,11.1f", s.avgLoadTime())).append('|');
            sb.append(String.format("%,11d", s.getMaxLoadTime())).append('\n');
        }
        printSepLine(sb, title);
        return sb;
    }
 
    private String getName(String name) {
        if (name == null) {
            return null;
        }
        if (name.length() > maxNameLength) {
            return "..." + name.substring(name.length() - maxNameLength + 3);
        } else {
            return name;
        }
    }
}

还需要配置logback,看上文

6、指标的解读

以下是 JetCache 的各项统计指标的解读:

  1. 缓存命中率(Hit Rate):表示缓存中已经存在的数据占总请求次数的比例。命中率越高,说明缓存效果越好,反之则说明缓存效果较差。

  2. 缓存请求次数(Request Count):表示系统对缓存的请求总次数,包括读取和写入操作。

  3. 缓存命中次数(Hit Count):表示系统从缓存中读取数据的次数。

  4. 缓存未命中次数(Miss Count):表示系统从缓存中读取数据失败的次数,需要从数据库或其他数据源中获取数据。

  5. 缓存写入次数(Put Count):表示系统向缓存中写入数据的次数。

  6. 缓存删除次数(Remove Count):表示系统从缓存中删除数据的次数。

  7. 缓存平均读取时间(Average Read Time):表示系统从缓存中读取数据的平均时间。

  8. 缓存平均写入时间(Average Write Time):表示系统向缓存中写入数据的平均时间。

  9. 缓存平均删除时间(Average Remove Time):表示系统从缓存中删除数据的平均时间。

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值