Rack cache 指导
rack-cache 是一个方便,优雅的 HTTP 缓存。
有点像 varnish,但是方便多了。用过 varnish,语法糟糕而且系统还会 crash。不推荐。作为 Ruby 开发人员,要尽量呆在 Ruby 社区。外面的世界太可怕了。
安装
在 Gemfile 里添加
gem 'rack-cache'
在 config/environments/production.rb 里添加:
config.action_dispatch.rack_cache = {
metastore: "memcached://localhost:11211/meta",
entitystore: "file:tmp/cache/rack/body",
verbose: true,
allow_reload: true,
allow_revalidate: true
}
你也可以放在 config/application.rb 里,不过语法有一点区别,原因参见 Github issue.
config.middleware.insert_before(Rack::ConditionalGet, Rack::Cache, {
metastore: "memcached://localhost:11211/meta",
entitystore: "file:tmp/cache/rack/body",
verbose: true,
allow_reload: true,
allow_revalidate: true
})
配置
metastore 是元数据,放在 memcached 里很快。
entitystore 我使用的 file system store,够用了。如果用 redis 和 memcached 可能会有意外问题,比如 redis 大量数据启动很慢,memcached 有最大单体容量闲置,默认是只有几兆。
verbose 是日志,打印在 stdout 的,所以 log/developement.log 里没有的。
reload 的意思是用户按住 shift 加点 refresh,这种情况一般不会出现啦。
revalidate 是用户点 refresh。
reload 不顾一切,一定要重新取一遍数据。revalidate 呢,是 rack-cache 一定要去重新算一个 etag (etag 就是页面的 hash 啦),然后和 browser 端比较一下,有可能不传输的。
allow_reload 和 allow_revalidate 的默认值都是 false,所以我们在这里都改为 true。
allow_reload: true,
allow_revalidate: true,
好啦。你相当于有一个 cache proxy 啦。
使用
在你的 rails controller 里,加入
expires_in 1.day
这样,这样,不仅浏览器会 cache,rack-cache 这个 middleware proxy 也会 cache!
所以一个用户请求了之后,另外一个用户,可以直接从 rack-cache 里读取到值。
注意,用户点 fresh 会 revalidate,在地址栏按回车,相当于 refresh,也会 revalidate。
Rails 用户要注意,如果你有 protect_from_forgery,默认的 app/views/layout/application.html.erb 上有 <%= csrf_meta_tag %>,这个每次请求页面都是新的,所以 etag 每次都会不同,浏览器会认为页面有更新,还是会重新获取一份。我的方法是把 csrf_meta_tag 拿掉。
如果还要用 protect_from_forgery,可以在需要的页面使用 hidden_field_tag :authenticity_token, form_authenticity_token
,比如:
<%= form_tag "/" do %>
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
...
<%= submit_tag 'Sign In', class: 'button short-button' %>
<% end %>
会员
除非是静态资源,否则就不要用 HTTP cache 了。
如果游客和注册用户,都通过同一个 URL 访问内容,HTTP cache 就控制不了。其实不光是 URL 上的内容,同一个 URL 如果有不同的 controller 逻辑也不行,因为 HTTP cache 会导致不去请求 controller。
一个常见的解决方法是 subdomain,比如会员使用 my.abc.com。
可以在 controller 里用 root_url(host: "my." + request.domain)
进行跳转。
当然,注意 cross subdomain cookies 是默认关闭的,要去 config/initializers/session_store.rb 里加入 domain: :all。否则你的程序会变成两个独立的服务,flash[:notice] 什么的会出问题。
Application.config.session_store :cookie_store, key: '_app_session', domain: :all