如何防止Rails 3.1将静态资产缓存到Rails.cache?

我在我的Rails 3.1应用程序上使用CloudFlare CDN。 Cloudflare是一种在DNS级别工作的CDN。 在第一次打到静态资产时,CloudFlare会从您的应用程序加载它,然后将其缓存在CDN中。 从CDN而不是您的应用程序加载该资产的未来请求。

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

config.action_controller.perform_caching = true 

它启用了Rack :: Cache中间件。 由于Rails为静态资产设置了默认缓存控制设置,因此这些资产将写入Rails.cache存储。 结果,我的缓存存储(在我的情况下是redis)被填充了静态资产,其中url作为哈希键。

遗憾的是,我无法关闭静态资产缓存控制标头,而不会影响Cloudflare和我的用户的浏览器如何缓存资产。 我无法关闭控制器缓存或丢失页面/操作/片段缓存。 如果我删除Rack :: Cache中间件,结果相同。

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

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

原始海报希望防止静态资产进入常规Rails缓存,这导致他们想要禁用Rack :: Cache。 而不是这样做,更好的解决方案是配置Rack :: Cache使用单独的缓存而不是一般的Rails缓存。

Rack :: Cache应针对实体存储与元存储进行不同的配置。 Rack :: Cache有两个不同的存储区域:元存储和实体存储。 Metastore保存有关每个缓存条目的高级信息,包括HTTP请求和响应头。 该区域存储以高频率访问的小块数据。 实体存储缓存响应主体内容,该内容可以是相对大量的数据,尽管它的访问频率低于元数据库。

以下配置将memcached中的Metastore信息缓存到文件系统的实际资产主体中。

使用memcached gem:

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

使用dalli gem

 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

经过大量的实验,我最终在我的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 :: Cache请求之前添加一个Rack :: Static机架中间件。 Rack :: Static中间件为根目录提供匹配前缀的URL。 在这里,我将config.assets.prefix作为我的url前缀,默认为’/ assets。’ 我将root设置为’public’目录。

请求此路径:

/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js

应该在这个文件中找到它:

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

这应该直接从public / assets目录中提供任何资产,而不是完全按Rails :: Cache,这将阻止它将资产存储在Rails cache_store中。 这只有在生产中运行’rake assets:precompile’时才有效,否则’public / assets’中不会有预编译资产。

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

 config.assets.cache_store = :null_store 

这应该保持Sprockets不缓存任何东西。

另一种解决相同问题的方法就是使用ActionDispatch :: Static中间件代替Rack :: Static,如下所示:

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

你问的Rack :: Static和ActionDispatch :: Static有什么区别?

  • Rack :: Static采用一组url前缀来检查请求url。 所以在我们的例子中,如果请求路径以’/ assets’开头,它只会检查文件。

  • 无论路径如何,ActionDispatch :: Static都会在每个GET / HEAD请求中检查“public”中是否存在该文件。

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

  • 如果ActionDispatch :: Static在其路径中找不到该文件,它将继续沿着机架中间件链(Rails堆栈的其余部分)继续运行。

最后,无论ActionDispatch :: Static在’public’中找不到它,它都只会传递给Rails堆栈。 所以Rails最终将提供ActionDispatch :: Static无法找到的资产。 这解决了Rack :: Cache找不到资产的问题,但由于每个请求都会触发文件检查,因此资源也更加密集。

Interesting Posts