Code Monkey home page Code Monkey logo

multilevel-cache-spring-boot-starter's People

Contributors

aeizzz avatar chunmenglu avatar flyinwind1 avatar lishangbu avatar lltx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

multilevel-cache-spring-boot-starter's Issues

CacheMessageListner可能会将当前节点缓存删除

虽然测试中出现的概率几乎为0,但是处理的逻辑确实有可能出现这种情况:
在代码逻辑中:

push(new CacheMessage(this.name, key))
caffeineCache.put(key, value)

push(new CacheMessage(this.name, key))在CacheMessageListener的回调方法中,并没有过滤掉当前节点,可能会导致当前节点的本地caffeine缓存也删除,当然,这个情况可以说几乎不存在(因为走网络和走内存几乎是走内存快),但是更加严谨的做法,应该是处理消息时,避开当前节点

默认使用全局过期时间,报错

private void doPut(Object key, Object value) {
	Duration expire = getExpire();
	value = toStoreValue(value);
	if (!expire.isNegative()) {
		stringKeyRedisTemplate.opsForValue().set(getKey(key), value, expire);
	}
	else {
		stringKeyRedisTemplate.opsForValue().set(getKey(key), value);
	}

	push(new CacheMessage(this.name, key));

	caffeineCache.put(key, value);
}

在doPut方法中,默认不设置时间则采用全局过期时间,全局过期时间设置为0,默认不过期,但在这里走的是stringKeyRedisTemplate.opsForValue().set(getKey(key), value, expire);这条语句,导致报错。

No CacheResolver specified

No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary or declare a specific CacheManager to use.
at org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:224)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:914)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)

建议添加根据key前缀删除缓存

  • 缓存名称:cacheName:key1-key2-key3
  • 例子:查询多条集合数据、与更新一条数据
    • 根据cacheName:key1-key2-key3(id-类型-数量)返回集合数据
    • 根据cacheName:key1-key2(id-类型)更新一条数据
  • 建议添加更新完一条数据后,设置所有缓存key前缀为cacheName:key1-key2的缓存数据失效功能

关于RedisCaffeineCache里的get(Object key, Callable<T> valueLoader)方法

public <T> T get(Object key, Callable<T> valueLoader) {
        ....
	ReentrantLock lock = keyLockMap.get(key.toString());
	if (lock == null) {
		log.debug("create lock for key : {}", key);
		lock = new ReentrantLock();
		keyLockMap.putIfAbsent(key.toString(), lock);
	}
	try {
		lock.lock();
		....
	}
	catch (Exception e) {
		throw new ValueRetrievalException(key, valueLoader, e.getCause());
	}
	finally {
		lock.unlock();
	}
}

关于下方这几句是否有线程安全问题:
lock = new ReentrantLock();
keyLockMap.putIfAbsent(key.toString(), lock);
lock.lock();

假设线程T1、T2同时获取lock为null,且都进入了
if(lock == null) 方法体。
T1、T2分别执行了lock = new ReentrantLock(); 假设T1为lock1,T2位lock2。
接着T1成功把lock1放入keyLockMap,T2放入时就会失败。
再下方的代码lock.lock(),T1和T2分别拿着自己的lock同时执行了需要同步的代码块。

下面是我的认为的解决方法

public <T> T get(Object key, Callable<T> valueLoader) {
        ....
	ReentrantLock lock = keyLockMap.get(key.toString());
	if (lock == null) {
		log.debug("create lock for key : {}", key);
		lock = new ReentrantLock();
		ReentrantLock  templock = keyLockMap.putIfAbsent(key.toString(), lock);
                lock = templock == null ? lock : templock;
	}
	try {
		lock.lock();
		....
	}
	catch (Exception e) {
		throw new ValueRetrievalException(key, valueLoader, e.getCause());
	}
	finally {
		lock.unlock();
	}
}

为什么redis不用hash类型存储呢?

按理说 cacheManage使用方式,用hash存储更契合,与caffeine里面的类型也更兼容
过期时间的问题,caffeine中设置的是整个cacheName的过期时间,而redis设置的是每个key的过期时间,是否会有问题呢

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.