spring 提供了spring-cache上层接口给大家实现,其中有一些方便操作缓存的注解,诸如@Cacheable、@CacheEvict等等。今天就来学习一下redis的实现 spring-data-redis.
配置缓存需要配置一个CacheManager
public interface CacheManager {
/**
* Get the cache associated with the given name.
* <p>Note that the cache may be lazily created at runtime if the
* native provider supports it.
* @param name the cache identifier (must not be {@code null})
* @return the associated cache, or {@code null} if such a cache
* does not exist or could be not created
*/
@Nullable
Cache getCache(String name);
/**
* Get a collection of the cache names known by this manager.
* @return the names of all caches known by the cache manager
*/
Collection<String> getCacheNames();
}
这个顶层接口有一个抽象类AbstractCacheManager
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private volatile Set<String> cacheNames = Collections.emptySet();
// Early cache initialization on startup
@Override
public void afterPropertiesSet() {
initializeCaches();
}
/**
* Initialize the static configuration of caches.
* <p>Triggered on startup through {@link #afterPropertiesSet()};
* can also be called to re-initialize at runtime.
* @since 4.2.2
* @see #loadCaches()
*/
public void initializeCaches() {
Collection<? extends Cache> caches = loadCaches();
synchronized (this.cacheMap) {
this.cacheNames = Collections.emptySet();
this.cacheMap.clear();
Set<String> cacheNames = new LinkedHashSet<>(caches.size());
for (Cache cache : caches) {
String name = cache.getName();
this.cacheMap.put(name, decorateCache(cache));
cacheNames.add(name);
}
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
}
/**
* Load the initial caches for this cache manager.
* <p>Called by {@link #afterPropertiesSet()} on startup.
* The returned collection may be empty but must not be {@code null}.
*/
protected abstract Collection<? extends Cache> loadCaches();
// Lazy cache initialization on access
@Override
@Nullable
public Cache getCache(String name) {
// Quick check for existing cache...
Cache cache = this.cacheMap.get(name);
if (cache != null) {
return cache;
}
// The provider may support on-demand cache creation...
Cache missingCache = getMissingCache(name);
if (missingCache != null) {
// Fully synchronize now for missing cache registration
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = decorateCache(missingCache);
this.cacheMap.put(name, cache);
updateCacheNames(name);
}
}
}
return cache;
}
@Override
public Collection<String> getCacheNames() {
return this.cacheNames;
}
// Common cache initialization delegates for subclasses
/**
* Check for a registered cache of the given name.
* In contrast to {@link #getCache(String)}, this method does not trigger
* the lazy creation of missing caches via {@link #getMissingCache(String)}.
* @param name the cache identifier (must not be {@code null})
* @return the associated Cache instance, or {@code null} if none found
* @since 4.1
* @see #getCache(String)
* @see #getMissingCache(String)
*/
@Nullable
protected final Cache lookupCache(String name) {
return this.cacheMap.get(name);
}
/**
* Dynamically register an additional Cache with this manager.
* @param cache the Cache to register
* @deprecated as of Spring 4.3, in favor of {@link #getMissingCache(String)}
*/
@Deprecated
protected final void addCache(Cache cache) {
String name = cache.getName();
synchronized (this.cacheMap) {
if (this.cacheMap.put(name, decorateCache(cache)) == null) {
updateCacheNames(name);
}
}
}
/**
* Update the exposed {@link #cacheNames} set with the given name.
* <p>This will always be called within a full {@link #cacheMap} lock
* and effectively behaves like a {@code CopyOnWriteArraySet} with
* preserved order but exposed as an unmodifiable reference.
* @param name the name of the cache to be added
*/
private void updateCacheNames(String name) {
Set<String> cacheNames = new LinkedHashSet<>(this.cacheNames);
cacheNames.add(name);
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
// Overridable template methods for cache initialization
/**
* Decorate the given Cache object if necessary.
* @param cache the Cache object to be added to this CacheManager
* @return the decorated Cache object to be used instead,
* or simply the passed-in Cache object by default
*/
protected Cache decorateCache(Cache cache) {
return cache;
}
/**
* Return a missing cache with the specified {@code name}, or {@code null} if
* such a cache does not exist or could not be created on demand.
* <p>Caches may be lazily created at runtime if the native provider supports it.
* If a lookup by name does not yield any result, an {@code AbstractCacheManager}
* subclass gets a chance to register such a cache at runtime. The returned cache
* will be automatically added to this cache manager.
* @param name the name of the cache to retrieve
* @return the missing cache, or {@code null} if no such cache exists or could be
* created on demand
* @since 4.1
* @see #getCache(String)
*/
@Nullable
protected Cache getMissingCache(String name) {
return null;
}
}
各个厂商都会提供一个对应的CacheManager实现这个抽象类,例如redis的RedisCacheManager.
首先我们对AbstractCacheManager进行解读,看看里面都有些什么操作。
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private volatile Set<String> cacheNames = Collections.emptySet();
这两个成员变量存的是缓存map和缓存名称。
@Override
public void afterPropertiesSet() {
initializeCaches();
}
实现了InitializingBean在装在这个bean的时候调用initializeCaches();方法进行初始化。下面看一下这个方法。
public void initializeCaches() {
// 调用抽象方法loadCaches();获取缓存
Collection<? extends Cache> caches = loadCaches();
// 线程安全的初始化成员变量cacheMap和cacheNames
synchronized (this.cacheMap) {
// 初始化为空
this.cacheNames = Collections.emptySet();
this.cacheMap.clear();
Set<String> cacheNames = new LinkedHashSet<>(caches.size());
// 获取诸如配置文件中配置的缓存名称
for (Cache cache : caches) {
//获取缓存名称
String name = cache.getName();
// 装饰之后放入map中
this.cacheMap.put(name, decorateCache(cache));
// 将缓存名称放入cacheNames
cacheNames.add(name);
}
// 将cacheNames设置成不可修改的set集合
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
}
看一下初始化的缓存从哪里来的。看看这个方法的定义。
protected abstract Collection<? extends Cache> loadCaches();
看一下RedisCacheManager中的实现。
@Override
protected Collection<RedisCache> loadCaches() {
List<RedisCache> caches = new LinkedList<>();
// 从配置中获取配置的缓存
for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
// 封装成RedisCache放在集合里
caches.add(createRedisCache(entry.getKey(), entry.getValue()));
}
return caches;
}
这里的initialCacheConfiguration的定义如下:
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
左边的String是缓存名称。
我们在配置文件中配置cache-names等等最终就会放在这个initialCacheConfiguration里面。具体放进去的过程下次写整合springCache的redis实现的时候介绍他的自动装配过程以及redis的缓存实现。配置文件中比如这样写:
#配置spring-cache
spring:
cache:
# 缓存使用的实现是redis
type: redis
# 初始化缓存的两个名称
#如果这里配置了那系统里@Cacheable注解的缓存就只能是这些缓存,
#没有配置就是自动动态创建 下文会介绍
cache-names: 'demo,test'
#对redis的一些配置
redis:
#自动生成key的前缀
key-prefix: 'tabtan:'
#默认过期时间
time-to-live: 3600000
这其中cache-names中的每一个缓存名称就会作为initialCacheConfigurationMap的键。根据一些其他的配置封装成的缓存就会成为对应的值。
回过头来再看初始化方法initializeCaches()开头的loadCaches()方法。
Collection<? extends Cache> caches = loadCaches();
redis的实现把你配置的caches封装成了List<RedisCache>返回到了这里。
至此初始化就完成。(RedisCache实现了Cache)
在说其他方法之前,我想说这种具体实现延迟到子类的方式是十分常用的,可以借鉴学习。
下面开始介绍CacheManager接口的第一个方法Cache getCache(String name);
看一下接口中如何定义
/**
* Get the cache associated with the given name.
* 获取与给定名称关联的缓存。
* <p>Note that the cache may be lazily created at runtime if the
* native provider supports it.
* @param name the cache identifier (must not be {@code null})
* @param name 缓存名称 不能为空
* @return the associated cache, or {@code null} if such a cache
* does not exist or could be not created
*/
@Nullable
Cache getCache(String name);
看一下抽象类中的实现
@Override
@Nullable
public Cache getCache(String name) {
// 获取初始化后的cacheMap看看有没有这个缓存 有直接返回 没有就getMissingCache(name)动态创建
Cache cache = this.cacheMap.get(name);
if (cache != null) {
return cache;
}
// The provider may support on-demand cache creation...
// 上边的英文注释意思大致是 实现可能支持按需创建缓存
//我们今天看的是redis实现 是支持动态按需创建的
// 同样这个getMissingCache提供给子类实现 但不是抽象方法。
Cache missingCache = getMissingCache(name);
// 判断一下getMissingCache(name)之后是不是空 空的话就返回空的
if (missingCache != null) {
// Fully synchronize now for missing cache registration
// 上锁,防止注册丢失
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
// double check
if (cache == null) {
// 装饰好按需创建的
cache = decorateCache(missingCache);
// 放到map里
this.cacheMap.put(name, cache);
// 更新缓存的名字 这个方法是借口的第二个方法
updateCacheNames(name);
}
}
}
return cache;
}
看一下getMissingCache(name)的定义。
@Nullable
protected Cache getMissingCache(String name) {
return null;
}
默认返回null,意思就是不支持按需创建缓存,没有把这个方法定义成抽象方法的原因就在这里,如果具体实现不想支持按需创建缓存的话就不需要实现这个方法。redis是支持的我们看一下具体实现。
@Override
protected RedisCache getMissingCache(String name) {
// 默认支持动态创建就createRedisCache(name, defaultCacheConfig)创建 如果配置关闭了动态创建就直接
return allowInFlightCacheCreation ? createRedisCache(name, defaultCacheConfig) : null;
}
关闭动态创建的配置方法为disableCreateOnMissingCache()
public RedisCacheManagerBuilder disableCreateOnMissingCache() {
this.allowInFlightCacheCreation = false;
return this;
}
值得关注的是,这个关闭动态创建缓存的方法只在RedisCacheManagerBuilder能配置,配置文件里没有 你想配置的话可以自己写。调用这个方法组合cache-names可以限制系统中的缓存名称只能是在cache-names配置中的这些。
我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:
尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot
一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
当我尝试进行bundle安装时,我的gem_path和gem_home指向/usr/local/rvm/gems/我没有写入权限,并且由于权限无效而失败。因此,我已将两个路径都更改为我具有写入权限的本地目录。这样做时,我进行了bundle安装,我得到:bruno@test6:~$bundleinstallFetchinggemmetadatafromhttps://rubygems.org/.........Fetchinggemmetadatafromhttps://rubygems.org/..Bundler::GemspecError:Couldnotreadgemat/afs/
我一直在Heroku上尝试不同的缓存策略,并添加了他们的memcached附加组件,目的是为我的应用程序添加Action缓存。但是,当我在我当前的应用程序上查看Rails.cache.stats时(安装了memcached并使用dalligem),在执行应该缓存的操作后,我得到current和total_items为0。在Controller的顶部,我想缓存我有的Action:caches_action:show此外,我修改了我的环境配置(对于在Heroku上运行的配置)config.cache_store=:dalli_store我是否可以查看其他一些统计数据,看看它是否有效或我做错
我有一个具有页面缓存的ControllerAction,我制作了一个清扫程序,它使用Controller和指定的Action调用expire_page...Controller操作呈现一个js.erb模板,所以我试图确保expire_page删除public/javascripts中的.js文件,但它没有这样做。classJavascriptsController"javascripts",:action=>"lol",:format=>'js')endend...所以,我访问javascripts/lol.js并呈现我的模板。我验证了public/javascripts/lol.js
我的Controller有这个:caches_action:render_ticker_for_channel,:expires_in=>30.seconds在我的路由文件中我有这个:match'/render_c_t/:channel_id'=>'render#render_ticker_for_channel',:as=>:render_channel_ticker在日志文件中我看到了这个:Writefragmentviews/mcr3.dev/render_c_t/63(11.6ms)我如何手动使它过期?我需要从与渲染Controller不同的Controller使它过期,但即使
我在开发和生产中都使用docker,真正困扰我的一件事是docker缓存的简单性。我的ruby应用程序需要bundleinstall来安装依赖项,因此我从以下Dockerfile开始:添加GemfileGemfile添加Gemfile.lockGemfile.lock运行bundleinstall--path/root/bundle所有依赖项都被缓存,并且在我添加新gem之前效果很好。即使我添加的gem只有0.5MB,从头开始安装所有应用程序gem仍然需要10-15分钟。由于依赖项文件夹的大小(大约300MB),然后再花10分钟来部署它。我在node_modules和npm上遇到了
我正在使用Capybara2.1和Ruby1.9.3,使用selenium驱动程序(带有Minitest和测试单元)来测试网络应用程序。我正在努力解决StaleElementReferenceException问题。我已经看到很多关于该主题的讨论,但我无法找到解决我所面临问题的方法。所以基本上,我试图使用以下代码在我的页面上找到所有分页元素:pagination_elements=page.all('.paginationa')然后我对这些元素做一些断言,例如:pagination_elements.first.must_have_content('1')在这些断言之后,我通过单击下一