J2Cache源码阅读(二)---CacheChannel

J2Cache二级缓存框架源码阅读

因为最近在看Redis的设计与实现,想写一个用java实现的缓存框架,于是就找了一个开源的J2Cache框架源码来学习,这里是记录的第一篇文章。



一.CacheObject

CacheObject,是面向用户的一个缓存对象,所有的缓存返回值,都是封装成CacheObject,先来看一下它的成员变量,应该非常容易理解。

public class CacheObject {

	public final static byte LEVEL_1 	 = 1;	//一级缓存数据
	public final static byte LEVEL_2 	 = 2;	//二级缓存数据
	public final static byte LEVEL_OUTER = 3;	//外部数据

	// 获取数据所在的缓存区域
	private String region;
	// 缓存数据键值
	private String key;
	// 返回实际缓存的对象
	private Object value;
	// 缓存所在的层级
	private byte level;
	
	...
}

二.CacheChannel

CacheChannel,是所有的缓存操作提供者,是一个抽象类。

先来看它的成员变量

  • _g_keyLocks : 在CacheChannel使用CHM集合来实现,作用是为了并发控制,防止在多线程的情况下,多个线程同时来获取数据,在一级缓存或者二级缓存失效的情况下,发生缓存穿透,它可以在最先进入的线程前加入以region%key命名的key-v键值对,在后面的线程访问时发现存在该key,则被阻塞,当最先进入的线程访问数据并写入缓存后,所有数据直接读取缓存。
  • config : J2Cache的配置文件。
  • holder : J2Cache的缓存管理器,可以获取一级缓存管理器和二级缓存管理器。
  • defaultCacheNullObject : 是否开启缓存空对象,它会在构造方法初始化时,根据config配置的来决定是否开启缓存空对象。
  • closed : 管道是否关闭。
	private static final Map<String, Object> _g_keyLocks = new ConcurrentHashMap<>();
	private J2CacheConfig config;
	private CacheProviderHolder holder;
    private boolean defaultCacheNullObject ;
    private boolean closed;

再来看它的构造方法通用方法
构造方法进行初始化的赋值,没有什么特殊点。
另外两个方法,newNullObject返回一个空对象,用于缓存空对象时使用,assertNotClose检查管道是否关闭,在所有方法执行前都会调用。

	public CacheChannel(J2CacheConfig config, CacheProviderHolder holder) {
		this.config = config;
		this.holder = holder;
		this.defaultCacheNullObject = config.isDefaultCacheNullObject();
		this.closed = false;
	}

	private NullObject newNullObject() {
		return new NullObject();
	}

	private void assertNotClose() {
		if(closed)
			throw new IllegalStateException("CacheChannel closed");
	}

还有两个抽象方法,主要作用是为了广播,在集群模式中使用。

	protected abstract void sendClearCmd(String region);
	protected abstract void sendEvictCmd(String region, String...keys);

剩下就是它执行缓存的crud操作了,先来看它是如何获取缓存的,看它的读取缓存的get方法。
使用时需要传入它的region区域,key名称,还有可变参数的是否缓存空对象boolean数组。

这里我也不知道为什么要传入可变参数,感觉一个boolean值就可以用了,希望了解的兄弟可以留个言告诉我一下,谢谢啦。

方法的具体流程都在注释上写了,首先会检查管道是否关闭,关闭则直接抛出异常,如果没有则先new出要返回的CacheObject缓存对象。

首先先从一级缓存尝试获取数据,当一级缓存没有获取到,则在_g_keyLocks中加锁,主要是为了并发控制,防止缓存穿透。

然后尝试从二级缓存中获取数据,当二级缓存也没有获取到时,会根据是否缓存空对象来决定是否需要缓存一个空对象在一二级缓存中。

/**
	 * 读取缓存(用户无需判断返回的对象是否为空)
	 * @param region Cache region name
	 * @param key Cache data key
     * @param cacheNullObject 是否缓存空对象
	 * @return cache object
	 */
	public CacheObject get(String region, String key, boolean...cacheNullObject)  {
		// 判断管道是否关闭
		this.assertNotClose();
		// 创建要返回的缓存对象
		CacheObject obj = new CacheObject(region, key, CacheObject.LEVEL_1);
		// 从一级缓存中对应的region获取对应key
		obj.setValue(holder.getLevel1Cache(region).get(key));
		// 一级缓存存在则直接返回
		if(obj.rawValue() != null)
			return obj;

		/**
		 * 这里进行加锁 是为了防止并发问题,保证在一级缓存没有读取到时,
		 * 其他线程阻塞,在当前线程走完流程并成功赋值一级缓存后,
		 * 删除锁,然后其他线程读取一级缓存
		 */
		String lock_key = key + '%' + region;
		synchronized (_g_keyLocks.computeIfAbsent(lock_key, v -> new Object())) {
			// 再一次获取
			obj.setValue(holder.getLevel1Cache(region).get(key));
			if(obj.rawValue() != null)
				return obj;
			// 尝试获取二级缓存
			try {
				obj.setLevel(CacheObject.LEVEL_2);
				obj.setValue(holder.getLevel2Cache(region).get(key));
				if (obj.rawValue() != null) {
					// 获取到二级缓存,则赋值给一级缓存
					holder.getLevel1Cache(region).put(key, obj.rawValue());
				}else {
					// 仍然没有获取到二级缓存,判断是否需要缓存空对象【防止缓存穿透】,根据传参决定
					boolean cacheNull = (cacheNullObject.length > 0) ? cacheNullObject[0] : defaultCacheNullObject;
					if (cacheNull)
						set(region, key, newNullObject(), true);
				}
			} finally {
				// 删除锁
				_g_keyLocks.remove(lock_key);
			}
		}
		// 返回缓存对象
		return obj;
	}

其他的操作根据上面的流程也是差不多的。

总结

这个框架主要是基于已经实现的缓存框架和缓存中间件进行封装整合形成二级缓存框架,大多数缓存操作还是应该看一下诸如ehcache等缓存框架,redis等缓存中间件,明确了学习目标,就继续学习吧。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
J2Cache是OSChina目前正在使用的两级缓存框架(要求至少 Java 8)。第一级缓存使用内存(同时支持 Ehcache 2.x、Ehcache 3.x 和 Caffeine),第缓存使用 Redis(推荐)/Memcached。由于大量的缓存读取会导致L2的网络成为整个系统的瓶颈,因此L1的目标是降低对L2的读取次数。该缓存框架主要用于集群环境中。单机也可使用,用于避免应用重启导致的缓存冷启动后对后端业务的冲击。 数据读取: 读取顺序 -> L1 -> L2 -> DB 数据更新 1、从数据库中读取最新数据,依次更新L1 -> L2 ,发送广播清除某个缓存信息 2、接收到广播(手工清除缓存 & 一级缓存自动失效),从L1中清除指定的缓存信息 J2Cache配置: 配置文件位于core/resources目录下,包含三个文件: j2cache.properties J2Cache核心配置文件,可配置两级的缓存,Redis服务器、连接池以及缓存广播的方式 caffeine.properties如果一级缓存选用Caffeine ,那么该文件用来配置缓存信息 ehcache.xml Ehcache的配置文件,配置说明请参考Ehcache文档 ehcache3.xml Ehcache3的配置文件,配置说明请参考Ehcache文档 network.xml JGroups网络配置,如果使用JGroups组播的话需要这个文件,一般无需修改 实际使用过程需要将所需的配置文件复制到应用类路径中,如WEB-INF/classes目录。 J2Cache运行时所需jar包请查看core/pom.xml 测试方法: 1、安装Redis 2、git clone https://gitee.com/ld/J2Cache 3、修改core/resource/j2cache.properties配置使用已安装的Redis服务器 4、在命令行中执行mvn package -DskipTests=true进行项目编译 5、打开多个命令行窗口,同时运行runtest.sh 6、在 > 提示符后输入help查看命令,并进行测试
Resilience4j-cache 是一个基于 Resilience4j 的缓存库,提供了对缓存的重试、熔断和限流等功能。它可以很方便地与 Spring Boot 集成使用。 以下是使用 Resilience4j-cache 的步骤: 1. 添加依赖 在 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-cache</artifactId> <version>1.7.0</version> </dependency> ``` 2. 配置缓存 在 Spring Boot 的配置文件中添加以下配置: ``` resilience4j.cache.caffeine.instances.myCache.maximum-size=1000 resilience4j.cache.caffeine.instances.myCache.expire-after-write=5s ``` 该配置表示创建一个名为 `myCache` 的缓存,最大容量为 1000,写入后 5 秒过期。 3. 创建缓存 在代码中创建缓存实例: ``` Cache<String, String> cache = Cache.of("myCache", CacheConfig .custom() .expireAfterWrite(Duration.ofSeconds(5)) .maximumSize(1000) .build() ); ``` 4. 使用缓存 使用 `cache.get(key, loader)` 方法获取缓存项,如果缓存不存在,则会调用 `loader` 方法加载数据。 ``` String value = cache.get("key", () -> { // 从数据库或其他地方加载数据 return "value"; }); ``` 5. 配置重试、熔断和限流 可以使用 Resilience4j 提供的 `Retry`、`CircuitBreaker` 和 `RateLimiter` 等组件对缓存进行重试、熔断和限流等操作。 例如,使用 `Retry` 组件对缓存进行重试操作: ``` Cache<String, String> cache = Cache.of("myCache", CacheConfig .custom() .expireAfterWrite(Duration.ofSeconds(5)) .maximumSize(1000) .build() ); Retry retry = Retry.ofDefaults("retry"); Function<String, String> decorated = Retry.decorateFunction(retry, cache::get); String value = decorated.apply("key"); ``` 以上就是使用 Resilience4j-cache 的基本步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舍其小伙伴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值