【问题标题】:What would cause Rails page caching to stop working?什么会导致 Rails 页面缓存停止工作?
【发布时间】:2009-08-21 00:10:24
【问题描述】:

我有一个 Rails 应用程序在某处停止缓存,我不确定是哪个版本导致它无法工作。

我的印象是页面缓存在正常工作的情况下,如果找到缓存的文件,它甚至不应该命中 Rails。但是,当加载我的页面并监控 production.log 时,它会同时影响 Rails 和 DB。

我设置了一个清扫器,用于清除 :create、:update 和 :destroy 上的缓存。它工作正常,因为 /public/cache/index.html 文件会在其中一个事件发生时更新。起初我以为可能是因为我使用的是 OutputCompression 插件,但删除它有相同的结果,所以我把它放回去了。 index.html 在那里,但是 .htaccess 和 Rails 忽略它并重建整个页面,包括重写缓存的 index.html。

以下是代码的相关部分(除非我遗漏了什么):

控制器:

class SecretsController < ApplicationController
  caches_page :index
  cache_sweeper :secret_sweeper, :only => [:create, :update, :destroy]

  # snipped
end

.htaccess:

RewriteEngine On

# Rewrite index to check for cached
RewriteRule ^/$ /cache/index.html [QSA]
RewriteRule ^$ /cache/index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]

Firebug 响应标头

Date: Tue, 02 Jun 2009 18:50:36 GMT
Server: Apache/1.3.41 (Unix) mod_fastcgi/2.4.2 PHP/5.2.9 mod_log_bytes/1.2 mod_bwlimited/1.4 mod_auth_passthrough/1.8 FrontPage/5.0.2.2635 mod_ssl/2.8.31 OpenSSL/0.9.8b
Vary: Accept-Encoding
X-Runtime: 0.05637
Etag: "4f3497a74141d1e92ae7a1fe4d5dc1d2"
Cache-Control: private, max-age=0, must-revalidate
Content-Encoding: gzip
Content-Length: 22356
Connection: close
Content-Type: text/html; charset=utf-8
default-style: tms

我希望能够使用 mod_gzip,但 ASmallOrange 不支持它,而 DreamHost 支持(在他们将我的价格翻了三倍之前)。

无论如何,谁能解释为什么 Rails 忽略了缓存的 index.html?我假设它是 .htaccess 中的东西,因为如果它工作正常,它不应该接触 Rails。

编辑:缓存问题原来是 RewriteRules 上的第一个斜线。在我将它们都更改为“cache/index.html”之前,它没有找到缓存文件,现在缓存工作完美。

但是,现在我必须删除 OutputCompression 调用,因为它返回的是 Content-Type 设置为“text/html”的文件的 gzip 压缩版本。知道如何让它为该文件发送正确的内容类型吗?它是整个应用程序中唯一缓存的一个。

再次编辑:将 .htaccess 更改为此对 gzip 问题没有帮助:

RewriteRule ^/$ cache/index.html [QSA,T=application/x-gzip]
RewriteRule ^$ cache/index.html [QSA,T=application/x-gzip]

除非禁用压缩,否则它仍会显示为 zip 文件的文本表示形式(即乱码)。不过,缓存效果很好。

【问题讨论】:

  • 看到应该创建的index.html文件了吗?
  • @fd:是的,如第三段所述,它在 /public/cache/index.html 中正确创建。如果创建、更新或销毁事件发生,该文件将被擦除并替换为新生成的文件,再次包含正确的内容。当我加载页面,然后单击指向相同 URL(站点的基础)的链接时,.htaccess 应该使用缓存版本,但事实并非如此。我希望 Ctrl+F5 强制刷新(也许),但这只是两次单击指向主索引页面的相同链接。
  • 相当令人费解,RewriteRules 看起来很有说服力。所以我会默认检查我的假设。 .htaccess 是否被正确提取? mod_rewrite 是否已安装并正常工作?等等。Ofc,我希望答案是肯定的,因为你以某种方式进入 Rails,但我通常会通过挑战我认为我知道的关于故障系统的真实情况来发现一些有趣的事情。

标签: ruby-on-rails ruby .htaccess caching


【解决方案1】:

为什么要使用 OutputCompression 插件? Apache 可以为您做到这一点。查看mod_deflate

这是我使用的规则:

# Deflate
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript application/x-java
script
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

这将压缩来自应用程序的所有文本输出,静态和动态。

只是一个猜测,但我想这也更快,因为它是一个用 C 编写的 Apache 模块。

【讨论】:

  • 如果我不在 ASmallOrange 的共享主机上,那肯定行得通,它禁用了 mod_deflate 和 mod_gzip。 OutputCompression 做同样的事情,并使用相同的处理器时间,所以我不知道他们为什么不启用它。还是)感谢你的建议。如果我能说服他们为我启用 mod_deflate,我会使用它。
  • 哦,真可惜。这会大大减少他们的带宽。新主机? ;) 您可能想尝试让您的应用程序升级到 Heroku 的免费级别。即使是他们最低的付费水平也与共享主机的成本大致相同。
  • 感谢您的建议。刚刚更新了一年,因为我喜欢 ASO,并且我正在用 Django 重写我的应用程序,这样我就可以复习我的 Python(当然还有学习 Django)。知道任何免费托管吗?
  • 您可以通过一些工作让 Django 在 Google App Engine 上运行。他们的免费级别非常慷慨。
【解决方案2】:

我会检查您的 ETag 配置。如果您使用多个 Web 服务器并且未将其配置为独立于提供文件的计算机,这通常会阻止文件的正确缓存。

【讨论】:

  • 它应该只是共享主机上的单个 Web 服务器,我听说过 ETag,但不知道它们是什么或如何设置它们。有什么指点吗?
【解决方案3】:

终于解决了问题,我的网站在停机 10 周后再次上线。一旦我得到它来加载缓存文件,我发现另一个问题是 OutputCompression 插件将文件压缩为 .gz 文件,但 Rails 将其保存为 .html,而 Apache 将其作为 text/html 提供,结果胡言乱语。

解决了我的问题的修复:

在 .htaccess 中:

AddEncoding x-gzip .gz
AddType text/html .gz

RewriteRule ^/$ cache/index.gz [QSA]
RewriteRule ^$ cache/index.gz [QSA]

在 config/environments.rb 中:

ActionController::Base.page_cache_extension = ".gz"

Ruby 代码将“caches”指令保存为“cache/index.gz”而不是“cache/index.html”。 AddEncoding 告诉它不要将其作为 html 提供,但它本身只会显示页面源,因为它默认为“text/plain”的 Content-Type。 AddType 改变了一些东西,以便 .gz 文件作为“text/html”提供,从而导致正确显示。

这可能不适用于所有人,但由于我不在网站上的任何地方提供 .gz 文件,而且首页是唯一缓存的,这对我来说非常适合。

感谢大家的帮助。

【讨论】: