如何防止 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 此处 上开立了一张票证。
I'm using CloudFlare CDN on my Rails 3.1 application. Cloudflare is a CDN that works at the DNS level. On the first hit to a static asset, CloudFlare loads it from your app then caches it in their CDN. Future requests for that asset load from the CDN instead of your app.
The problem I'm having is that if you set controller caching to true:
config.action_controller.perform_caching = true
it enables the Rack::Cache middleware. Since Rails sets a default cache control setting for static assets, those assets get written to the Rails.cache store. As a result my cache store (in my case redis) is being filled up with static assets with the url as the hash key.
Unfortunately, I can't turn off the static asset cache control headers without affecting how Cloudflare and my users' browsers cache the assets. I can't turn off controller caching or I lose page/action/fragment caching. Same result if I delete the Rack::Cache middleware.
Does anyone have any other ideas?
Update: I've opened a ticket on GitHub here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
经过大量实验后,我最终在 config/application.rb 中执行了此操作:
它的作用是在向 Rack::Cache 发出请求之前添加 Rack::Static 机架中间件。 Rack::Static 中间件提供带有根目录匹配前缀的 URL。在这里,我将 config.assets.prefix 作为我的 url 前缀,默认为“/assets”。我将根目录设置为“public”目录。
对此路径的请求:
/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js
应该在此文件中找到它:
public/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js
这应该服务任何直接从public/assets 目录而不是根本访问 Rails::Cache,这将阻止它将资产存储在 Rails cache_store 中。仅当您在生产中运行“rake assets:precompile”时,这才有效,否则“public/assets”中将没有预编译资产。
After a lot of experimentation, I've ended up doing this in my config/application.rb:
What this does is add a Rack::Static rack middleware before requests to Rack::Cache. The Rack::Static middleware serves up urls with a matching prefix to a root directory. Here I'm giving config.assets.prefix as my url prefix which defaults to '/assets.' I'm setting the root to the 'public' directory.
Requests for this path:
/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js
should find it in this file:
public/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js
This should serve any assets directly out of the public/assets directory instead of hitting Rails::Cache at all, which will prevent it from storing the assets in the Rails cache_store. This will only work if you run the 'rake assets:precompile' in production, otherwise there will be no precompiled assets in 'public/assets'.
最初的发布者想要防止静态资源进入一般 Rails 缓存,这导致他们想要禁用 Rack::Cache。与其这样做,更好的解决方案是将 Rack::Cache 配置为使用与通用 Rails 缓存不同的单独缓存。
对于实体存储和元存储,Rack::Cache 应该进行不同的配置。 Rack::Cache 有两个不同的存储区域:元存储和实体存储。元存储保留有关每个缓存条目的高级信息,包括 HTTP 请求和响应标头。该区域存储高频访问的小块数据。实体存储缓存响应正文内容,尽管其访问频率低于元存储,但该内容可能是相对大量的数据。
以下配置将元存储信息缓存在 memcached 中,但将资产的实际主体缓存到文件系统中。
使用 memcached gem:
使用 dalli gem
顺便说一句,这个配置是 Heroku 的推荐:
https://devcenter.heroku.com/articles/rack-cache-memcached-static-资产rails31
The original poster wanted to prevent static assets from getting into the general Rails cache, which led them to want to disable the Rack::Cache. Rather than doing this, the better solution is to configure Rack::Cache to use a separate cache than the general Rails cache.
Rack::Cache should be configured differently for entity storage vs meta storage. Rack::Cache has two different storage areas: meta and entity stores. The metastore keeps high level information about each cache entry including HTTP request and response headers. This area stores small chunks of data that is accessed at a high frequency. The entitystore caches the response body content which can be a relatively large amount of data though it is accessed less frequently than the metastore.
The below configuration caches the metastore info in memcached but the actual body of the assets to the file system.
Using memcached gem:
Using dalli gem
By the way this configuration is the recommendation for Heroku:
https://devcenter.heroku.com/articles/rack-cache-memcached-static-assets-rails31
您可以关闭资产管道文件的缓存,同时保留其他缓存:
这应该可以防止 Sprockets 缓存任何内容。
You can turn off caching of asset pipeline files while leaving other caching in place with:
That should keep Sprockets from caching anything.
解决相同问题和此问题的另一种方法是使用 ActionDispatch::Static 中间件而不是 Rack::Static,如下所示:
您问 Rack::Static 和 ActionDispatch::Static 之间有什么区别?
Rack::Static 采用一组 url 前缀来检查请求 url。因此,在我们的例子中,它只会检查请求路径以“/assets”开头的文件。
ActionDispatch::Static 将在每个 GET/HEAD 请求中检查“public”中文件是否存在,无论路径如何。
Rack::Static 不会首先检查文件,它会在文件上调用 Rack::File.new,因此如果文件不存在,则会返回 404,不会将请求传递给中间件chain.
如果 ActionDispatch::Static 在其路径中没有找到该文件,它将继续沿着机架中间件链(Rails 堆栈的其余部分)向下移动。
最后,无论 ActionDispatch::Static 在“public”中找不到什么,它都会传递到 Rails 堆栈。所以 Rails 最终将提供 ActionDispatch::Static 找不到的资源。这解决了 Rack::Cache 找不到资产的问题,但它也更加消耗资源,因为每个请求都会触发文件检查。
Another way to solve the same problem and this issue is to use the ActionDispatch::Static middleware instead of Rack::Static like this:
What's the difference between Rack::Static and ActionDispatch::Static you ask?
Rack::Static takes an array of url prefixes to check against the request url. So in our case, it will only check for files if the request path begins with '/assets'.
ActionDispatch::Static will check for the existence of the file in 'public' on every GET/HEAD request, regardless of path.
Rack::Static doesn't check for the file first, it calls Rack::File.new on the file, so if it doesn't exist it will return a 404, it will not pass the request down the middleware chain.
If ActionDispatch::Static doesn't find the file in its path, it'll continue down the rack middleware chain (the rest of the Rails stack).
In the end, whatever ActionDispatch::Static doesn't find in 'public' it'll just pass on down to the Rails stack. So Rails will end up serving the assets that ActionDispatch::Static can't find. This solves my issue of assets not being found by Rack::Cache, but it's also more resource intensive since every request will trigger a file check.