可能里面的一些知识已经不被大家使用了,但是作为学习,我想和大家分享一下个人关于 Rails.cache 的浅显的认识,望大家指教。
它是 Rails 中的缓存,拥有所有缓存的共同点,它是为了提升网站性能。
本身常用的有四种,可以根据不同的环境进行选择不同的存储系统。
这里我以使用 redis 来作为 Rails.cache 的存储系统进行介绍
因为是 key-value 的结构,同时使用 redis,能够通过 redis 的命令快速实现,比关系型数据库拥有更快的读写速度,且更适合储存非结构化数据。
配置信息(必须):
config.action_controller.perform_caching = true # 确保开启缓存,dev环境下默认是没有开启的。
config.cache_store = :redis_store, {
host: 'localhost',
port: '6379',
db: 1, # 这是整数,可以理解为redis数据库中的表标志,默认是16个数据库,可从0-15中取值
password: '',
expires_in: 5.hours # 过期时间的设置
}
production 上默认开启了缓存
development 上默认不开启
配置方法一(常用):
# 在development.rb文件中添加
config.action_controller.perform_caching = true
# 设置缓存的页面存放的地址(不能修改,默认是public)
config.action_controller.page_cache_directory = "#{Rails.root.to_s}/public"
配置方法二(Rails5 版本之后开始支持):
# 执行任务,会在tmp文件下创建caching-dev.txt和restart.txt文件
rake dev:cache
经过个人测试,两者不能混用,混用存在的问题: 1.如果你使用 rake dev:cache,就不能在 development.rb 中去修改缓存默认存储的地址,一旦修改,缓存就会失效。 2.一旦你是用 rake dev:cache 来开启缓存,那么就要使用 rake dev:cache 来关闭缓存(不能通过 config.action_controller.perform_caching = false 来操作,无法起作用),如果要自己测试,那你一定要首先把之前缓存的文件给删除掉,不然不管你怎么操作,只要 public 下有该文件,就一直读该文件。
如果看一下 rake dev:cache 的源码,我们能找到另外一种方式来关闭
# 源码 rake任务
namespace :dev do
desc "Toggle development mode caching on/off"
task :cache do
Rails::DevCaching.enable_by_file
end
end
# 源码:通过文件来判断是否开启缓存
FILE = "tmp/caching-dev.txt"
def enable_by_file
FileUtils.mkdir_p("tmp")
if File.exist?(FILE)
delete_cache_file
puts "Development mode is no longer being cached."
else
create_cache_file
puts "Development mode is now being cached."
end
FileUtils.touch "tmp/restart.txt"
end
所以,很明显,我们可以通过删除 tmp 下的 restart.txt 和 cache-dev.txt 来关闭缓存。 个人建议:还是在 development.rb 文件中自己配置比较灵活
从 Rails4 开始,页面缓存和方法缓存已经转换为 gem 的形式,只有片段缓存是 Rails 默认的。
一句话总结:使用缓存的页面来代替 action 请求对应的页面,这个和缓存系统无关。 默认缓存的文件直接放在 public 目录下。
页面缓存现在已经被单独作为一个 gem,需要在你的 Gemfile 中加入 gem "actionpack-page_caching"
真正的代码实现:
# 一个控制器
class HuanCunController < ApplicationController
# 使用缓存
caches_page :welcome_show
# 缓存的页面对应的控制器
def welcome_show
end
end
welcome_show 页面 Hello,welcom!! current_time_now:<%= Time.now.strftime('%Y-%m-%d %H:%M:%S.%L') %>
没有使用缓存的时候,每次请求的页面都是实时变化的,可以看 current_time_now 的值。 使用缓存后,会在你配置保存目录的文件下生成对应的静态 html 文件。
在 Gemfile 文件中加入 gem 'actionpack-action_caching' 为了便于观察,选择 redis 作为缓存系统。
使用方法缓存的代码如下:
class Car::CarsController < ApplicationController
caches_action :index
def index
end
end
监控方法:
打开终端,输入 redis-cli,然后输入 monitor,可以监控 redis 的存储情况。 调用该 index 方法,会产生一个 key:"get""views/locahost:3003/car/cars"
以后每次再去调用该 index,你会发现 redis 的 monitor 中都是显示如下信息:说明缓存成功,不再每次去方法中执行,而是直接从 redis 中去取。
1560408837.780396 [2 [::1]:54380] "get" "views/localhost:3003/car/cars"
1560408838.140465 [2 [::1]:54380] "get" "views/localhost:3003/car/cars"
1560408838.667096 [2 [::1]:54380] "get" "views/localhost:3003/car/cars"
过期时间的问题:
如果你没有设置过期时间,那么他的过期时间使用 redis 设置的过期时间。
如果想查看过期时间,进入 redis 控制台,然后选择你使用的数据库 id,例如 (查出来的时间是以秒为单位)
myMacBook-Pro redis-cli
127.0.0.1:6379>select 2
OK
127.0.0.1:6379[2]> TTL "views/localhost:3003/car/cars"
(integer) 13620
127.0.0.1:6379[2]>=
如果想自己设置过期时间,可以直接查看 gem 'actionpack-action_caching'这个 gem 的文档
在index.html.erb页面中
<% cache 'test_index' do %>
<%= Time.now.strftime('%Y-%m-%d %H:%M:%S.%L') %>
<% end %>
这个其实和方法缓存的监控的方式一样,你会发现会生成"views/car/cars/index:61696bc621e5ee68531c6a58dfaf4da3/test_index"这样的 key,同时也能查到过期时间。 每次你去刷新页面,都会在 redis 中去请求这个 key 值对应的 value 值。 如果想自己设置过期时间为 2 小时,可以这样设置
<% cache 'test_index',expires_in: 2.hours do %>
<%= Time.now.strftime('%Y-%m-%d %H:%M:%S.%L') %>
<% end %>
如果你想让这个"views/car/cars/index:61696bc621e5ee68531c6a58dfaf4da3/test_index"的 key 失效,你可以使用 expire_fragment('views/car/cars/index:61696bc621e5ee68531c6a58dfaf4da3/test_index') ,但是在实际应用中我们无法查到这个 key 的具体值,所以我们可以这样设置:
<% cache 'test_index', {skip_digest: true} do %>
<%= Time.now.strftime('%Y-%m-%d %H:%M:%S.%L') %>
<% end %>
这样’test_index’将会被作为 cache key 被缓存起来,如果想要使他失效,只要在对应的 action 里调用就 expire_fragment('test_index') 就行,如果不加 {skip_digest: true},那么是无法直接调用 expire_fragment('test_index') 的。 可以简单的查看一下生成这个片段 name 的源码:
def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil)
if skip_digest
name
else
fragment_name_with_digest(name, virtual_path)
end
end
可以很明显的看出,如果skip_digest是true,将直接返回页面中写的name。