【问题标题】:How do I prevent Rails 3.1 from caching static assets to Rails.cache?如何防止 Rails 3.1 将静态资产缓存到 Rails.cache?
【发布时间】:2011-10-21 05:32:57
【问题描述】:

我在我的 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 here 上开了一张票。

【问题讨论】:

  • 当您说静态资产时,您是指 Sprockets 生成的文件吗?
  • 是的,我愿意。将哈希附加到文件名。

标签: ruby-on-rails caching ruby-on-rails-3.1 asset-pipeline cloudflare


【解决方案1】:

经过大量实验,我最终在我的 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”。我将根目录设置为“公共”目录。

对该路径的请求:

/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js

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

public/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js

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

【讨论】:

  • 一个随机的旁白 - 你如何让你的资产 ID 出现在文件名中?
  • 您是否设置config.assets.precompile 包含您要预编译的所有文件?
  • @Kevin 你可以在你的production.rb中设置config.action_controller.perform_caching = true guides.rubyonrails.org/asset_pipeline.html#in-production
【解决方案2】:

解决相同问题的另一种方法是使用 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 请求时检查“公共”文件是否存在,无论路径如何。

  • Rack::Static 不会先检查文件,它会在文件上调用 Rack::File.new,所以如果它不存在会返回 404,它不会通过请求沿着中间件链。

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

最后,无论 ActionDispatch::Static 在 'public' 中找不到什么,它都会向下传递到 Rails 堆栈。所以 Rails 最终会为 ActionDispatch::Static 找不到的资产提供服务。这解决了我的 Rack::Cache 找不到资产的问题,但它也更加占用资源,因为每个请求都会触发文件检查。

【讨论】:

  • 当您说“每个请求都会触发文件检查”时——您是指对应用程序的每个请求,还是仅对公共请求?
  • 另外,在第一个要点中,您的意思是说 ActionDispatch::Static 吗?
  • 不,这是正确的。 Rack::Static 接受一个 url 前缀数组。它们的键甚至被称为“url”。
  • 如果您在每个要点中直接比较每个解决方案的行为,那将非常有帮助 - 现在这有点模棱两可。
  • 我有点希望这会在 Rails 中得到修复,并且这只是一个临时修复。有机会我会考虑重新格式化它。
【解决方案3】:

最初的发布者想要阻止静态资源进入通用 Rails 缓存,这导致他们想要禁用 Rack::Cache。与其这样做,更好的解决方案是将 Rack::Cache 配置为使用单独的缓存而不是一般的 Rails 缓存。

Rack::Cache 应该针对实体存储和元存储进行不同的配置。 Rack::Cache 有两个不同的存储区域:元存储和实体存储。 Metastore 保存有关每个缓存条目的高级信息,包括 HTTP 请求和响应标头。该区域存储以高频率访问的小块数据。 entitystore 缓存了响应体内容,虽然访问的频率比 metastore 低,但可能是相对大量的数据。

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

使用 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

【讨论】:

    【解决方案4】:

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

    config.assets.cache_store = :null_store
    

    这应该可以防止 Sprockets 缓存任何东西。

    【讨论】:

    • 这在 Rails 3.2 上对我有用,因为上述所有答案只让我走到了一半。谢谢!
    猜你喜欢
    • 2013-05-19
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    • 2018-05-19
    • 2011-11-04
    • 1970-01-01
    • 2012-09-02
    • 2012-03-31
    相关资源
    最近更新 更多