掘金 后端 ( ) • 2024-04-19 12:02

背景

项目部署重新启动时,用户访问系统出现鉴权超时,严重影响运营效率(ps:受到了客户diss)。主要原因是项目重新启动获取权限配置时间较长。权限配置需要查询公司的鉴权中心,本地获取到后进行解析并存入本地缓存(caffeine cache)。因此需要对权限缓存进行优化,最简单的解决方式是直接加一个redis缓存,但在gitee上有更好的二级缓存解决方案——J2Cache。借此机会学习下这个二级缓存框架。

1、简述

1.1、概述

J2Cache 是 OSChina 目前正在使用的两级缓存框架(要求至少 Java 8)。第一级缓存使用内存(同时支持 Ehcache 2.x、Ehcache 3.x 和 Caffeine),第二级缓存使用 Redis(推荐)/Memcached 。 由于大量的缓存读取会导致 L2 的网络成为整个系统的瓶颈,因此 L1 的目标是降低对 L2 的读取次数。 该缓存框架主要用于集群环境中。单机也可使用,用于避免应用重启导致的缓存冷启动后对后端业务的冲击。详细介绍请看J2Cache官网内容

1.2、设计思路

Ehcache和Redis结合起来,将Ehcache(也可以使用caffeine cache)作为一级缓存、将redis(也可以使用memcached )作为二级缓存,取长补短。尽量从本机取数据,取不到的时候再去redis里面取。这样结合可以保证高性能。数据基本上都是从Ehcache里面取得,有效的缓解应用冷启动对数据库的压力,应用和redis之间不会频繁的有大量数据传输。数据传输只存在应用冷启动及缓存变更时。

image.png

image.png

J2Cache 目前提供两种节点间数据同步的方案 —— Redis Pub/Sub 和 JGroups 。当某个节点的缓存数据需要更新时,J2Cache 会通过 Redis 的消息订阅机制或者是 JGroups 的组播来通知集群内其他节点。当其他节点收到缓存数据更新的通知时,它会清掉自己内存里的数据,然后重新从 Redis 中读取最新数据。这就完成了 J2Cache 缓存数据读写的闭环。

image.png

那么为什么不用 Ehcache 的集群方案?红薯大佬已经回答了:

对 Ehcache 比较熟悉的人还会问的就是这个问题,Ehcache 本身是提供集群模式的,可以在多个节点同步缓存数据。但是 Ehcache 的做法是将整个缓存数据在节点间进行传输。如咱们前面的说的,一个页面需要读取 50K 的缓存数据,当这 50K 的缓存数据有更新时,那么需要在几个节点间传递整个 50K 的数据。这也会造成应用节点间大量的数据传输,这个情况完全不可控。

补充:当然这个单个数据传输量本身并不比使用 J2Cache 多,但是 ehcache 利用 jgroups 来同步数据的做法,在实际测试过程中发现可靠性还是略低,而且 jgroups 的同步数据在云主机上无法使用。而 J2Cache 传输的仅仅是缓存的 key 而已,因此相比 Ehcache 的集群模式,J2Cache 要传输的数据极其小,对节点间的数据通信完全不会产生大的影响。

1.3、主要组成部分介绍

组成部分 介绍 Cache 缓存数据操作接口,有两个子类为【Level1Cache、Level2Cache】,各缓存分别实现Level1Cache、Level2Cache接口,具体如图enter description here转存失败,建议直接上传图片文件 ClusterPolicy 缓存集群策略接口,具体实现类如下enter description here转存失败,建议直接上传图片文件 CacheProviderHolder 两级缓存管理器。初始化L1、L2缓存以及缓存过期处理Listener CacheChannel 封装J2cahce缓存操作,对用户提供标准的缓存操作方法。如:get、exists、check、set、evict、clear等。 CacheProviderHolder 两级缓存管理器。初始化L1、L2缓存以及缓存过期处理Listener SpringRedisMessageListener J2Cache集成spring boot特有,监听redis缓存更新通知。 SpringRedisActiveMessageListener J2Cache集成spring boot特有,监听二级缓存key失效,主动清除本地缓存。 J2CacheCacheManger J2Cache集成spring boot特有。J2Cache集成SpringCache,可以使用SpringCache注解进行数据操作。

2、原理解析

由于项目使用的是SpringBoot,因此此处解析的是J2Cahce与SpringBoot相关内容。 项目中J2Cache的配置文件如下:

image.png

2.1、Spring容器注册CacheChannel

配置文件使用【@Configuration】注释作为注册CacheChanne Bean入口。

  • 注册主流程

image.png

  • 读取配置

image.png

  • 构建CacheChannel

image.png

  • 配置CacheChannel 如果缓存过期Listener能够支持扩展用户自定义逻辑更好。

image.png

  • 初始化集群消息通知机制

image.png

image.png

2.2、数据操作原理

2.2.1、存储缓存数据

代码中直接使用set方法即可存储数据,具体操作及redis数据存储结果如下:

image.png

redis中的数据

image.png

  • 设置数据

image.png

  • 命令发送 数据删除命令跟set数据方法在同一个类中。

image.png

数据删除、清空命令实现在创建CacheChannel中实现,命令发送具体内容在每个策略类中实现

image.png

  • 具体信息发送

image.png

image.png

image.png

redis消息队列

image.png

  • redis消息清除Listener

image.png

image.png

  • 清除本地一级缓存

image.png

至此,设置缓存数据流程已经完成,对于清空、删除数据操作命令通知也与上相同

2.2.2、读取数据缓存

获取数据直接使用get方法即可,具体操如下:

image.png

  • CacheChannel中get方法

image.png

需要关注的是,J2Cache 提供了一个可以传入数据加载器loader的方法。我们自定义取数逻辑,当一二级缓存没有数据时,采用此方法获取数据。

image.png

2.2.3、清空缓存数据

需要注意的是清除数据是将某个区域内所有的缓存数据清除掉,清空命令逻辑与设置数据删除命令的逻辑相同,详细可以看上面的设置数据

image.png

2.2.4、删除缓存数据

image.png

2.2.5、缓存key是否存在

exists与check是两个方法,我们可以查询是否存在,可以以单独调用查看缓存key在第几层级。

image.png

至此,缓存数据操作逻辑到此已经结束。

2.3、J2cache集成Spring Cache

J2cache集成了SpringCache,我们可以通过Spring cache的注释使用J2cahce。 项目中的配置需要增加【open-spring-cache】配置

image.png

J2cache源码中根据【open-spring-cache】配置选择性注册CacheManager,如下图

image.png

开发代码 #EE3F4D==中使用【@Cacheable】注释,这里要注意key的拼接方法。

image.png

执行日志如下图

image.png

3、使用demo

  • 引入jar包
	<dependency>
                <groupId>net.oschina.j2cache</groupId>
                <artifactId>j2cache-spring-boot2-starter</artifactId>
                <version>2.8.0-release</version>
            </dependency>
            <dependency>
                <groupId>net.oschina.j2cache</groupId>
                <artifactId>j2cache-core</artifactId>
                <version>2.8.0-release</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-simple</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-api</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
  • 添加J2Cache配置

image.png

  • 代码编写 CacheChannel使用【@Autowired】注释自动注入

image.png

image.png

4、总结

项目中使用J2Cache还是非常方便的,省去了自己去开发Caffeine Cache+Redis功能,而且最重要的是分布式项目本地缓存的一致性得到解决,大大提高开发效率。而且J2Cache与SpringCache注释结合大大减少了代码的侵入,非常推荐使用。