19

我在Rails 3.1应用程序上使用CloudFlare CDN。 Cloudflare是一款适用于DNS级别的CDN。在第一次打到静态资产时,CloudFlare会从您的应用程序加载它,然后将其缓存到CDN中。未来请求从CDN加载该资产而不是您的应用。如何防止Rails 3.1将静态资产缓存到Rails.cache?

我遇到的问题是,如果你设置控制器缓存为true:

config.action_controller.perform_caching = true 

它使机架::缓存中间件。由于Rails为静态资产设置了默认的缓存控制设置,这些资产被写入到Rails.cache存储中。因此,我的缓存存储(在我的情况下是redis)正在用URL作为散列键的静态资产填充。

不幸的是,我无法关闭静态资产缓存控制标头,而不会影响Cloudflare和我的用户的浏览器如何缓存资源。我无法关闭控制器缓存,或者我失去了页面/动作/片段缓存。如果我删除Rack :: Cache中间件,结果也是一样。

有没有人有任何其他想法?

更新:我已在GitHub here上打开了一张票。

+0

当你说静态资产时,你只是指Sprockets生成的文件? –

+0

是的,我喜欢。将哈希追加到文件名中。 –

回答

7

楼主想防止静电资产进入普通Rails的缓存,这导致他们要禁用的机架::缓存。与其执行此操作,更好的解决方案是将Rack :: Cache配置为使用单独的缓存而不是普通的Rails缓存。

架::缓存应该为不同的实体存储器VS元存储配置。 Rack :: Cache有两个不同的存储区域:元和实体存储。 Metastore保持有关每个缓存条目的高级信息,包括HTTP请求和响应头。该区域存储以高频率访问的小块数据。实体存储缓存响应主体内容,虽然访问频率低于元数据,但它可以是相对较大数据量的数据。

以下配置缓存在memcached中的metastore信息,但该资产的实际身体到文件系统。

使用memcached的宝石:

config.action_dispatch.rack_cache = { 
    :metastore => 'memcached://localhost:11211/meta', 
    :entitystore => 'file:tmp/cache/rack/body', 
    :allow_reload => false 
} 

使用达利宝石

config.action_dispatch.rack_cache = { 
    :metastore => Dalli::Client.new, 
    :entitystore => 'file:tmp/cache/rack/body', 
    :allow_reload => false 
} 

顺便说这个配置是的Heroku推荐: https://devcenter.heroku.com/articles/rack-cache-memcached-static-assets-rails31

8

经过大量的实验,我已经结束了在我的config/application.rb中这样做:

if !Rails.env.development? && !Rails.env.test? 
    config.middleware.insert_before Rack::Cache, Rack::Static, urls: [config.assets.prefix], root: 'public' 
end 

这样做是要求机架前添加一个机架::静态架中间件::缓存。 Rack :: Static中间件为URL提供了一个与根目录匹配的前缀。在这里,我将config.assets.prefix作为默认为'/ assets'的URL前缀。我将根设置为“公共”目录。

请求路径:

/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js

应该发现它在该文件中:

公共/资产/ jQuery的e8da439bbc8fd345e34ac57c6a216318.min .js

这应该直接从public/assets目录中取代Rails :: Ca因为它会阻止它将资产存储在Rails cache_store中。这只有在生产中运行'rake assets:precompile'时才有效,否则在'public/assets'中将没有预编译资产。

+0

一个随机放在一边 - 你如何得到你的资产ID显示在文件名中? – Kevin

+0

您是否已将'config.assets.precompile'设置为包含要预编译的所有文件? –

+0

@Kevin当你说“每个请求将触发一个文件检查”可以设置'config.action_controller.perform_caching = TRUE;在production.rb http://guides.rubyonrails.org/asset_pipeline.html#in-production – Schneems

1

另一种方法来解决同样的问题,这个问题是使用ActionDispatch ::静态中间件而不是机架的静::像这样:

if !Rails.env.development? && !Rails.env.test? 
    config.middleware.insert_before Rack::Cache, ::ActionDispatch::Static, 'public', config.static_cache_control 
end 

什么的机架::静态和ActionDispatch之间的区别: :你问静态?

  • Rack :: Static需要一个url前缀数组来检查请求url。因此,在我们的情况下,如果请求路径以'/ assets'开头,它将仅检查文件。

  • ActionDispatch :: Static将在每个GET/HEAD请求的'public'中检查文件是否存在,而不管路径如何。

  • Rack :: Static不会先检查文件,它会在文件上调用Rack :: File.new,所以如果它不存在,它将返回一个404,它不会传递请求中间件链。

  • 如果ActionDispatch ::静态没有找到在其路径的文件,它会继续向下机架中间件链(Rails的堆栈的其余部分)。

最后,无论ActionDispatch :: Static在'public'中找不到,它只会传递给Rails堆栈。因此,Rails将最终提供ActionDispatch :: Static无法找到的资产。这解决了我的资产没有被Rack :: Cache找到的问题,但是它也是资源密集型的,因为每个请求都会触发一次文件检查。

+0

- 你的意思是每个请求的应用程序,或只是要求公开? –

+0

另外,在第一个要点中,你的意思是说ActionDispatch :: Static? –

+1

不,这是正确的。 Rack :: Static需要一个url前缀数组。他们的关键甚至被称为“网址”。 –

3

您可以关闭资产管道缓存文件,同时保留其他缓存:

config.assets.cache_store = :null_store 

那应该保持链轮从缓存任何东西。

+0

在上面的所有答案只能让我在中途停下来之后,这对Rails 3.2有效。谢谢! –