rails cache

今天插一脚,介绍一下Rails的缓存机制以及memcached的使用
Rails的Cache分四种:
1,Page Cache - Fastest
2,Action Cache - Next Fastest
3,Fragment Cache - Least Fastest
4,ActiveRecord Cache - Only available in Edge Rails
下面一一介绍上面四种Cache以及Rails如何使用memcached

一、Page Cache
如果开发阶段要使用cache,则需要先设置好config/environments/development.rb:
Java 代码

1. config.action_controller.perform_caching = true

config.action_controller.perform_caching = true


而production环境下默认是开启cache功能的
Page Cache是Rails中最快的cache机制,使用Page Cache的前提一般为:
1,需要cache的page对所有用户一致
2,需要cache的page对public可访问,不需要authentication
Page Cache使用起来很简单:
Java 代码

1. class BlogController < ApplicationController
2. caches_page :list, :show
3.
4. def list
5. Post.find(:all, \:order => "created_on desc", :limit => 10)
6. end
7.
8. def show
9. @post = Post.find(params[:id])
10. end
11. end

class BlogController < ApplicationController
caches_page :list, :show

def list
Post.find(:all, \:order => "created_on desc", :limit => 10)
end

def show
@post = Post.find(params[:id])
end
end


这样我们就对BlogController的list和show页面进行了缓存
这样做的效果是第一次访问list和show页面时生成了public/blog/list.html和public/blog/show /5.html这两个html页面
对于分页情况下的cache,我们需要把url的page参数改写成"blog/list/:page"这种形式,而不是"blog /list?page=1"这种形式
这样cache的html页面即为public/blog/list/1.html
当数据更改时我们需要清除旧的缓存,我们采用Sweepers来做是非常不错的选择,这把在BlogController里清除缓存的代码分离出来
首先编辑config/environment.rb:
Java 代码

1. Rails::Initializer.run do |config|
2. # ...
3. config.load_paths += %w(#{RAILS_ROOT}/app/sweepers)
4. # ...

Rails::Initializer.run do |config|
# ...
config.load_paths += %w(#{RAILS_ROOT}/app/sweepers)
# ...


这告诉Rails加载#{RAILS_ROOT}/app/sweepers目录下的文件
我们为BlogController定义app/sweepers/blog_sweeper.rb:
Java 代码

1. class BlogSweeper < ActionController::Caching::Sweeper
2. observe Post # This sweeper is going to keep an eye on the Post model
3.
4. # If our sweeper detects that a Post was created call this
5. def after_create(post)
6. expire_cache_for(post)
7. end
8.
9. # If our sweeper detects that a Post was updated call this
10. def after_update(post)
11. expire_cache_for(post)
12. end
13.
14. # If our sweeper detects that a Post was deletedcall this
15. def after_destroy(post)
16. expire_cache_for(post)
17. end
18.
19. private
20. def expire_cache_for(record)
21. # Expire the list page now that we posted a new blog entry
22. expire_page(:controller => 'blog', :action => 'list')
23.
24. # Also expire the show page, in case we just edit a blog entry
25. expire_page(:controller => 'blog', :action => 'show', :id => record.id)
26. end
27. end

class BlogSweeper < ActionController::Caching::Sweeper
observe Post # This sweeper is going to keep an eye on the Post model

# If our sweeper detects that a Post was created call this
def after_create(post)
expire_cache_for(post)
end

# If our sweeper detects that a Post was updated call this
def after_update(post)
expire_cache_for(post)
end

# If our sweeper detects that a Post was deletedcall this
def after_destroy(post)
expire_cache_for(post)
end

private
def expire_cache_for(record)
# Expire the list page now that we posted a new blog entry
expire_page(:controller => 'blog', :action => 'list')

# Also expire the show page, in case we just edit a blog entry
expire_page(:controller => 'blog', :action => 'show', :id => record.id)
end
end


然后我们在BlogController里加上该sweeper即可:
Java 代码

1. class BlogController < ApplicationController
2. caches_page :list, :show
3. cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]
4. # ...
5. end

class BlogController < ApplicationController
caches_page :list, :show
cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]
# ...
end


我们可以配置cache的静态html文件的存放位置,这在config/environment.rb里设置:
Java 代码

1. config.action_controller.page_cache_directory = RAILS_ROOT + "/public/cache/"

config.action_controller.page_cache_directory = RAILS_ROOT + "/public/cache/"


然后我们设置Apache/Lighttpd对于静态html文件render时不接触Rails server即可
所以Page Cache就是最快的Cache,因为它不与Rails server打交道,直接load静态html

二、Action Cache
Action Cache相关的helper方法是caches_action和expire_action,其他基本和Page Cache一样
另外我们还可以运行rake tmp:cache:clear来清空所有的Action Cache和Fragment Cache
Java 代码

1. class BlogController < ApplicationController
2. before_filter :authentication
3. caches_action :list, :show
4. cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]

class BlogController < ApplicationController
before_filter :authentication
caches_action :list, :show
cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]


如上代码所示,我们将authentication这个filter放在caches_action之前声明,这样我们的Action Cache在执行之前会先访问authentication方法
这样可以弥补Page Cache不能对需要登录认证的Page进行Cache的缺点
生成的cache文件为tmp/cache/localhost:3000/blog/list.cache,这样对不同subdomain的访问页面可以cache到不同的目录
由于每次访问Action Cache时都需要与Rails server打交道,并且要先运行filters,所以比Page Cache的效率稍低

三、Fragment Cache
Fragment Cache用于处理rhtml页面中的部分需要cache的模块,如app/views/blog/list.rhtml:
Java 代码

1. <strong>My Blog Posts</strong>
2. <% cache do %>
3. <ul>
4. <% for post in @posts %>
5. <li><%= link_to post.title, :controller => 'blog', :action => 'show', :id => post %></li>
6. <% end %>
7. </ul>
8. <% end %>

<strong>My Blog Posts</strong>
<% cache do %>
<ul>
<% for post in @posts %>
<li><%= link_to post.title, :controller => 'blog', :action => 'show', :id => post %></li>
<% end %>
</ul>
<% end %>


生成的cache文件为/tmp/cache/localhost:3000/blog/list.cache
我们需要在BlogController的list方法里加上一行判断,如果是读取Fragment Cache,则不必再查询一次数据库:
Java 代码

1. def list
2. unless read_fragment({})
3. @post = Post.find(:all, \:order => 'created_on desc', :limit => 10)
4. end
5. end

def list
unless read_fragment({})
@post = Post.find(:all, \:order => 'created_on desc', :limit => 10)
end
end


Fragment分页时的Cache:
Java 代码

1. def list
2. unless read_fragment({:page => params[:page] || 1}) # Add the page param to the cache naming
3. @post_pages, @post = paginate :posts, :per_page => 10
4. end
5. end

def list
unless read_fragment({:page => params[:page] || 1}) # Add the page param to the cache naming
@post_pages, @post = paginate :posts, :per_page => 10
end
end


rhtml页面也需要改写:
Java 代码

1. <% cache ({:page => params[:page] || 1}) do %>
2. ... All of the html to display the posts ...
3. <% end %>

<% cache ({:page => params[:page] || 1}) do %>
... All of the html to display the posts ...
<% end %>


生成的cahce文件为/tmp/cache/localhost:3000/blog/list.page=1.cache
从分页的Fragment Cache可以看出,Fragment Cache可以添加类似名字空间的东西,用于区分同一rhtml页面的不同Fragment Cache,如:
Java 代码

1. cache ("turkey") => "/tmp/cache/turkey.cache"
2. cache (:controller => 'blog', :action => 'show', :id => 1) => "/tmp/cache/localhost:3000/blog/show/1.cache"
3. cache ("blog/recent_posts") => "/tmp/cache/blog/recent_posts.cache"
4. cache ("#{request.host_with_port}/blog/recent_posts") => "/tmp/cache/localhost:3000/blog/recent_posts.cache"

cache ("turkey") => "/tmp/cache/turkey.cache"
cache (:controller => 'blog', :action => 'show', :id => 1) => "/tmp/cache/localhost:3000/blog/show/1.cache"
cache ("blog/recent_posts") => "/tmp/cache/blog/recent_posts.cache"
cache ("#{request.host_with_port}/blog/recent_posts") => "/tmp/cache/localhost:3000/blog/recent_posts.cache"


清除Fragment Cache的例子:
Java 代码

1. expire_fragment(:controller => 'blog', :action => 'list', :page => 1)
2. expire_fragment(%r{blog/list.*})

expire_fragment(:controller => 'blog', :action => 'list', :page => 1)
expire_fragment(%r{blog/list.*})


四、ActiveRecord Cache
Rails Edge中ActiveRecord已经默认使用SQl Query Cache,对于同一action里面同一sql语句的数据库操作会使用cache

五、memcached
越来越多的大型站点使用memcached做缓存来加快访问速度
memcached是一个轻量的服务器进程,通过分配指定数量的内存来作为对象快速访问的cache
memcached就是一个巨大的Hash表,我们可以存取和删除key和value:
Java 代码

1. @tags = Tag.find :all
2. Cache.put 'all_your_tags', @tags
3. Cache.put 'favorite_skateboarder', 'Tom Penny'
4. skateboarder = Cache.get 'favorite_skateboarder'
5. Cache.delete 'all_your_tags'

@tags = Tag.find :all
Cache.put 'all_your_tags', @tags
Cache.put 'favorite_skateboarder', 'Tom Penny'
skateboarder = Cache.get 'favorite_skateboarder'
Cache.delete 'all_your_tags'


我们可以使用memcached做如下事情:
1,自动缓存数据库的一行作为一个ActiveRecord对象
2,缓存render_to_string的结果
3,手动存储复杂的数据库查询作为缓存
cached_model让我们轻松的缓存ActiveRecord对象,memcahed-client包含在cached_model的安装里,memcahed-client提供了缓存的delete方法
Java 代码

1. sudo gem install cached_model

sudo gem install cached_model


我们在config/environment.rb里添加如下代码来使用memcached:
Java 代码

1. require 'cached_model'
2.
3. memcache_options = {
4. :c_threshold => 10_000,
5. :compression => true,
6. :debug => false,
7. :namespace => 'my_rails_app',
8. :readonly => false,
9. :urlencode => false
10. }
11.
12. CACHE = MemCache.new memcache_options
13. CACHE.servers = 'localhost:11211'

require 'cached_model'

memcache_options = {
:c_threshold => 10_000,
:compression => true,
:debug => false,
:namespace => 'my_rails_app',
:readonly => false,
:urlencode => false
}

CACHE = MemCache.new memcache_options
CACHE.servers = 'localhost:11211'


对于production环境我们把上面的代码挪到config/environments/production.rb里即可
然后让我们的domain model集成CachedModel而不是ActiveRecord::Base
Java 代码

1. class Foo < CachedModel
2. end

class Foo < CachedModel
end


然后我们就可以使用memcached了:
Java 代码

1. all_foo = Foo.find :all
2. Cache.put 'Foo:all', all_foo
3. Cache.get 'Foo:all'

all_foo = Foo.find :all
Cache.put 'Foo:all', all_foo
Cache.get 'Foo:all'


需要注意的几点:
1,如果你查询的item不在缓存里,Cache.get将返回nil,可以利用这点来判断是否需要重新获取数据并重新插入到缓存
2,CachedModel的记录默认15分钟后expire,可以通过设置CachedModel.ttl来修改expiration time
3,手动插入的对象也会过期,Cache.put('foo', 'bar', 60)将在60秒后过期
4,如果分配的内存用尽,则older items将被删除
5,可以对每个app server启动一个memcached实例,分配128MB的内存对一般的程序来说足够

注:本文参考Ruby on Rails Caching Tutorial和memcached Basics for Rails两篇网文
BTW:Rails Envy上面有几个Ruby On Rails的广告视频,是关于与PHP/Java的比较,比较生动搞笑
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值