19

我在 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 上开了一张

4

4 回答 4

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::Cache 的请求之前添加一个 Rack::Static 机架中间件。Rack::Static 中间件将带有匹配前缀的 url 提供给根目录。在这里,我将 config.assets.prefix 作为我的 url 前缀,默认为“/assets”。我将根设置为“公共”目录。

此路径的请求:

/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js

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

公共/资产/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js

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

于 2011-08-09T23:06:15.953 回答
7

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

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

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

使用 memcached gem:

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

使用 dalli 宝石

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

于 2012-05-29T14:56:20.070 回答
3

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

config.assets.cache_store = :null_store

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

于 2013-06-21T23:44:36.433 回答
1

解决相同问题的另一种方法是使用 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 在“公共”中找不到什么,它都会传递给 Rails 堆栈。所以 Rails 最终会为 ActionDispatch::Static 找不到的资产提供服务。这解决了我的 Rack::Cache 找不到资产的问题,但它也更加占用资源,因为每个请求都会触发文件检查。

于 2012-02-17T19:53:45.193 回答