我有一个有趣的任务,我需要缓存我的方法的结果,这对于Spring缓存抽象非常简单
@Cachable(...)
public String getValue(String key){
return restService.getValue(key);
}
restService.getValue()以REST服务为目标,如果结束点关闭,可以回答。
我需要为缓存值设置一个特定的TTL,比方说5分钟,但如果服务器关闭,我需要返回最后一个值,即使它延长了5分钟。
我正在考虑使用第二个可缓存的方法,它没有TTL,并且总是返回最后一个值,如果restService没有返回任何内容,它将从getValue调用,但也许有更好的方法?
答案 0 :(得分:1)
我也有兴趣这样做一段时间。不好意思,我没有找到任何这样做的琐碎方式。 Spring不会为你做这个,它更多的是一个缓存实现spring是否可以包装的问题。我假设您正在使用EhCache实现。不幸的是,就我所知,这个功能并没有出现。
根据您的问题,有多种方法可以实现类似的目标
1)使用永久缓存时间并使用第二个类Thread,它会定期循环缓存数据以刷新它。我没有完全这样做,但Thread类需要看起来像这样:
@Autowired
EhCacheCacheManager ehCacheCacheManager;
...
//in the infinite loop
List keys = ((Ehcache) ehCacheCacheManager.getCache("test").getNative Cache()).getKeys();
for (int i = 0; i < keys.size(); i++) {
Object o = keys.get(i);
Ehcache ehcache = (Ehcache)ehCacheCacheManager.getCache("test").getNativeCache()
Element item = (ehcache).get(o);
//get the data based on some info in the value, and if no exceptions
ehcache.put(new Element(element.getKey(), newValue));
}
2)你可以让CacheListener监听驱逐事件,暂时存储数据。如果服务器调用失败,请使用该数据并从方法返回。
ehcache.xml
<cacheEventListenerFactory class="caching.MyCacheEventListenerFactory"/>
</cache>
</ehcache>
工厂: import net.sf.ehcache.event.CacheEventListener; import net.sf.ehcache.event.CacheEventListenerFactory; import java.util.Properties;
public class MyCacheEventListenerFactory extends CacheEventListenerFactory {
@Override
public CacheEventListener createCacheEventListener(Properties properties) {
return new CacheListener();
}
}
伪实现 import net.sf.ehcache.CacheException; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import net.sf.ehcache.event.CacheEventListener;
import java.util.concurrent.ConcurrentHashMap;
public class CacheListener implements CacheEventListener {
//prob bad practice to use a global static here - but its just for demo purposes
public static ConcurrentHashMap myMap = new ConcurrentHashMap();
@Override
public void notifyElementPut(Ehcache ehcache, Element element) throws CacheException {
//we can remove it since the put happens after a method return
myMap.remove(element.getKey());
}
@Override
public void notifyElementExpired(Ehcache ehcache, Element element) {
//expired item, we should store this
myMap.put(element.getKey(), element.getValue());
}
//....
}
3)付出了很多努力但是有效:
@Cacheable("test")
public MyObject getValue(String data) {
try {
MyObject result = callServer(data);
storeResultSomewhereLikeADatabase(result);
} catch (Exception ex) {
return getStoredResult(data);
}
}
这里的Pro是它可以在服务器重启之间工作,你可以简单地扩展它以允许集群服务器之间的共享缓存。 我在12集群环境中有一个版本,每个版本都先检查数据库,看看是否有任何其他集群首先得到“昂贵”的数据 然后重用它而不是进行服务器调用。
一个轻微的变体也是使用第二个@Cacheable方法和@CachePut而不是DB来存储数据。但这意味着内存使用量会翻倍。根据您的结果大小,这可能是可以接受的。
答案 1 :(得分:0)
也许你可以使用spel来改变使用过的缓存(一个使用ttl而第二个没有)如果条件(服务正常?)是真还是假,我从来没有用过这样的方法(我用它来根据一些请求参数更改密钥)但我认为它可以工作
@Cacheable(value = "T(com.xxx.ServiceChecker).checkService()",...)
其中checkService()是一个静态方法,它返回应该使用的缓存的名称