coldsmog开发笔记
JS 事件笔记
Ckeditor 上传WPS图片失败问题
Springboot
SpringBoot 统一异常处理
Springboot 引入外部jar包
Springboot 打成war包
Springboot 多环境配置
SpringBoot @Scope注解学习
Springboot 快速生成项目API文档
SpringCache 缓存
Spring jetcache 二级缓存
Springboot 按条件装配类
FastJson的JsonPath语法
正则表达式语法
Spring 路径匹配
Feign 基础数据操作
监控Feign调用metrics
Springboot feign的性能优化
Jackson 设置序列化属性
SpringBoot 集成 Spring Data Mongodb 操作 MongoDB
MongoDB 的一些注意事项
MongoDB 指令对比
Jackson 解析XML
Springboot Redis注册
SpringBoot RedisTemplate批量插入
Springboot 指标监控Micrometer
springboot validation 注解校验
springboot 引入配置
Springboot 静态文件处理
Springboot 导出csv文件
Springboot 事件驱动(发布/订阅模式)
Springboot 启动过程和扩展点
Springboot 优化停机上下线
Spring自动装配 - 干饭角度学习
Springboot ShardingJDBC
Springboot的重试
springboot 动态修改端口
Oracle
Oracle 中实现自增ID
Oracle 定时任务
Oracle 解锁临时表
Oracle 检查连接数
Oracle 表空间
Oracle 解释执行SQL
markdown作图(适用typora)
服务器压测
业务对象层和数据层
并发限流处理
中间件
Yarn的使用
Dubbo学习笔记-RPC扩展和本地Mock
Dubbo学习笔记-泛化实现进行mock
Redis缓存穿透,缓存击穿,缓存雪崩
Galera 集群说明
Pip 镜像
pip 使用
MySQL命令行
数据库缓存双写方案
Git相关操作
Redis 操作时间复杂度一览
nacos 杂记
mybatis 散记
shardingjdbc
一次线上事故排查发现的Caffeine缓存死锁问题
设计模式
重新讲讲单例模式和几种实现
更优雅地实现策略模式
Http-headers
Prometheus 杂散笔记
JAVA 散记
CompletableFuture
Gson、FastJson、Jackson、json-lib对比总结
jackson 时间的序列化踩坑
JVM
自定义注解
mysql类型和java类型 转换一览表
枚举维护一个Map<value, Enum>的映射
Java中String +、concat、StringBuilder、StringBuffer 性能对比
TraceId 使用
MySQL 多数据源处理
Mybatis-plus 流式查询
JAVA发送win 桌面通知
idea 启动项目失败非代码问题杂记
Lambda 简述
Arthas 使用笔记
一种链式更新数据的数据模式
Skywalking 新增中间件插件
Redission 使用
数据导出为图片
IDEA 的热重启
Netty 工具类
maven 插件
本文档使用 MrDoc 发布
-
+
首页
Spring 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/Caffeine cache类似。 ## 快速入门 ### 创建缓存实例 通过@CreateCache注解创建一个缓存实例,默认超时时间是100秒 ```java @CreateCache(expire = 100) private Cache<Long, UserDO> userCache; ``` 用起来就像map一样 ```java UserDO user = userCache.get(123L); userCache.put(123L, user); userCache.remove(123L); ``` 创建一个两级(内存+远程)的缓存,内存中的元素个数限制在50个。 ```java @CreateCache(name = "UserService.userCache", expire = 100, cacheType = CacheType.BOTH, localLimit = 50) private Cache<Long, UserDO> userCache; ``` name属性不是必须的,但是起个名字是个好习惯,展示统计数据的使用,会使用这个名字。如果同一个area两个@CreateCache的name配置一样,它们生成的Cache将指向同一个实例。 ### 创建方法缓存 使用@Cached方法可以为一个方法添加上缓存。JetCache通过Spring AOP生成代理,来支持缓存功能,注意避免AOP失效场景,比如同类调用。注解可以加在接口方法上也可以加在类方法上,但需要保证是个Spring bean。 ```java public interface UserService { @Cached(name="UserService.getUserById", expire = 3600) User getUserById(long userId); } ``` ## Spring Boot支持 如果使用Spring Boot,可以按如下的方式配置(这里使用了jedis客户端连接redis,也可以使用lettuce客户端)。 pom.xml ```xml <dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>${jetcache.latest.version}</version> </dependency> ``` [mvnrepository#jetcache-starter-redis](https://mvnrepository.com/artifact/com.alicp.jetcache/jetcache-starter-redis) 配置一个spring boot风格的application.yml文件,把他放到资源目录中 ``` jetcache: statIntervalMinutes: 15 areaInCacheName: false local: default: type: linkedhashmap keyConvertor: fastjson remote: default: type: redis keyConvertor: fastjson broadcastChannel: projectA valueEncoder: kryo valueDecoder: kryo poolConfig: minIdle: 5 maxIdle: 20 maxTotal: 50 host: 127.0.0.1 port: 6379 ``` 然后创建一个App类放在业务包的根下,EnableMethodCache,EnableCreateCacheAnnotation这两个注解分别激活Cached和CreateCache注解,其他和标准的Spring Boot程序是一样的。这个类可以直接main方法运行。 ```java 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方法缓存和SpringCache比较类似,它原生提供了TTL支持,以保证最终一致,并且支持二级缓存。JetCache2.4以后支持基于注解的缓存更新和删除。 在spring环境下,使用@Cached注解可以为一个方法添加缓存,@CacheUpdate用于更新缓存,@CacheInvalidate用于移除缓存元素。注解可以加在接口上也可以加在类上,加注解的类必须是一个spring bean,例如: ```java 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); } ``` key使用Spring的[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)脚本来指定。如果要使用参数名(比如这里的```key="#userId"```),项目编译设置target必须为1.8格式,并且指定javac的-parameters参数,否则就要使用```key="args[0]"```这样按下标访问的形式。 **@CacheUpdate和@CacheInvalidate的name和area属性必须和@Cached相同,name属性还会用做cache的key前缀**。 ### 二级缓存的机制 底层是 `MultiLevelCache`,本身是支持多级缓存的,目前默认是本地缓存+远程缓存。 这里不介绍过多的两级缓存的调度,感兴趣前往 `MultiLevelCache` 浏览相关代码。这里主要对二级缓存一致性做相关解读。 本地缓存和远程缓存的一致性,CacheRefresh 是一种固定同步的机制,但是refreshTime(刷新时间间隔)并不能保证数据最终一致,因为刷新有可能单机异常连不上redis,并且refresh时间如果过于频繁,对redis有不必要的压力。 jetcache对此的解法是 `CacheMonitor`的实例`CacheNotifyMonitor`,通过监听消息的变更,然后变更者发订阅消息,其他本地缓存监听到自己更新本地缓存。您可以参考`JetCacheAutoConfiguration::globalCacheConfig`修改全局配置,或者模仿`SpringConfigProvider::doInit`里操作CacheBuilderTemplate并注入上下文,他将在`JetCacheAutoConfiguration::cacheManager`中处理注入。 所以本地缓存时间,实际上是业务容忍多节点数据不一致的时间; 刷新时间间隔,实际上是业务容忍上下游数据不一致的时间,请按照这个思想去设计我们所需要的参数。 ## 注解说明 ### @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。如果注解上没有定义,则使用全局配置。| ### @Cached注解说明: |属性|默认值|说明| | --- | --- | --- | |area|“default”|如果在配置中配置了多个缓存area,在这里指定使用哪个area| |name|未定义|指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。| |key|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定key,如果没有指定会根据所有参数自动生成。| |expire|未定义| 超时时间。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为无穷大| |timeUnit|TimeUnit.SECONDS|指定expire的单位| |cacheType|CacheType.REMOTE|缓存的类型,包括CacheType.REMOTE、CacheType.LOCAL、CacheType.BOTH。如果定义为BOTH,会使用LOCAL和REMOTE组合成两级缓存| |localLimit|未定义|如果cacheType为LOCAL或BOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为100| |localExpire|未定义|仅当cacheType为BOTH时适用,为内存中的Cache指定一个不一样的超时时间,通常应该小于expire| |serialPolicy|未定义|指定远程缓存的序列化方式。可选值为SerialPolicy.JAVA和SerialPolicy.KRYO。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为SerialPolicy.JAVA| |keyConvertor|未定义|指定KEY的转换方式,用于将复杂的KEY类型转换为缓存实现可以接受的类型,当前支持KeyConvertor.FASTJSON和KeyConvertor.NONE。NONE表示不转换,FASTJSON可以将复杂对象KEY转换成String。如果注解上没有定义,会使用全局配置。| |enabled|true|是否激活缓存。例如某个dao方法上加缓存注解,由于某些调用场景下不能有缓存,所以可以设置enabled为false,正常调用不会使用缓存,在需要的地方可使用CacheContext.enableCache在回调中激活缓存,缓存激活的标记在ThreadLocal上,该标记被设置后,所有enable=false的缓存都被激活| |cacheNullValue|false|当方法返回值为null的时候是否要缓存| |condition|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定条件,如果表达式返回true的时候才去缓存中查询| |postCondition|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定条件,如果表达式返回true的时候才更新缓存,该评估在方法执行后进行,因此可以访问到#result| ### @CacheInvalidate注解说明: |属性|默认值|说明| | --- | --- | --- | |area|“default”|如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。| |name|未定义|指定缓存的唯一名称,指向对应的@Cached定义。| |key|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定key| |condition|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定条件,如果表达式返回true才执行删除,可访问方法结果#result| ### @CacheUpdate注解说明: |属性|默认值|说明| | --- | --- | --- | |area|“default”|如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。| |name|未定义|指定缓存的唯一名称,指向对应的@Cached定义。| |key|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定key| |value|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定value| |condition|未定义|使用[SpEL](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html)指定条件,如果表达式返回true才执行更新,可访问方法结果#result| **使用@CacheUpdate和@CacheInvalidate的时候,相关的缓存操作可能会失败(比如网络IO错误),所以指定缓存的超时时间是非常重要的**。 ### @CacheRefresh注解说明: 当缓存访问未命中的情况下,对并发进行的加载行为进行保护。 当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。 |属性|默认值|说明| | --- | --- | --- | |refresh|未定义|刷新间隔| |timeUnit|TimeUnit.SECONDS|时间单位| |stopRefreshAfterLastAccess|未定义|指定该key多长时间没有访问就停止刷新,如果不指定会一直刷新| |refreshLockTimeout|60秒|类型为BOTH/REMOTE的缓存刷新时,同时只会有一台服务器在刷新,这台服务器会在远程缓存放置一个分布式锁,此配置指定该锁的超时时间| ## 自定义扩展 ### 自定义序列化器 做一个类实现SerialPolicy接口,在Spring Context中创建该类的实例(假设名字为myBean),在Cached和CreateCache注解上设置serialPolicy="bean:myBean"即可 更进一步,如果想把自定义的序列化器设置为默认的,实现一个EncoderParser(继承DefaultSpringEncoderParser修改即可),然后做成一个bean放到 spring context中。 ### 自定义监控 ``` Cache orderCache = ... CacheMonitor orderCacheMonitor = new DefaultCacheMonitor("OrderCache"); // jetcache 2.2+, or call builder.addMonitor() before buildCache() // springboot 全局使用则是将Cache注册为 @Bean orderCache.config().getMonitors().add(orderCacheMonitor); // 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(); ``` 首先创建一个CacheMonitor,每个DefaultCacheMonitor只能用于一个Cache。当DefaultCacheMonitorManager启动以后,会使用slf4j按指定的时间定期输出统计信息到日志中,DefaultCacheMonitor构造时指定的名字会作为输出时cache的名字。 在组装多级缓存的过程中,可以给每个缓存安装一个Monitor,这样可以监控每一级的命中情况。 也可以自己对统计信息进行处理,调用下面的构造方法创建DefaultCacheMonitorManager: ``` public DefaultCacheMonitorManager(int resetTime, TimeUnit resetTimeUnit, Consumer<StatInfo> statCallback) ``` ## 一些注意事项 - 缓存DTO 需要实现序列化接口,否则可能只有本地缓存生效 - 2.7+刷新远程缓存时,会通知清空本地缓存,尽量指定`jetcache.remote.${area}.broadcastChannel`, 避免广播风暴 资料来源https://github.com/alibaba/jetcache/tree/master/docs/CN
寒烟濡雨
2024年4月15日 15:21
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
关于 MrDoc
觅思文档MrDoc
是
州的先生
开发并开源的在线文档系统,其适合作为个人和小型团队的云笔记、文档和知识库管理工具。
如果觅思文档给你或你的团队带来了帮助,欢迎对作者进行一些打赏捐助,这将有力支持作者持续投入精力更新和维护觅思文档,感谢你的捐助!
>>>捐助鸣谢列表
微信
支付宝
QQ
PayPal
Markdown文件
分享
链接
类型
密码
更新密码