rails cache 学习 - oyaji's Blog

rails cache 学习

oyaji posted @ 2011年1月26日 04:38 in rails with tags rails cache , 5561 阅读

 

在Rails中做缓存是简单的,要开启cache的话

config.action_controller.perform_caching = true 

默认情况下只有production是true,其他ENV都是false。

Rails中存储cache的方式多了去了,现在用的最多的应该是memcache吧。rails的guildes中有句Page caches are always stored on disk,貌似是所有的page cache都用filestore?在environment.rb中指定使用的store模式

ActionController::Base.cache_store = :file_store, "/path/to/cache/dir" 

只要上面两句话都配置好了,下面就简单的在需要cache的地方加几条语句就成

1、Fragment cache

就是缓存一个片段,将需要缓存的内容放入一个block中即可

<% cache do %>
  something to cache
<% end %>

如果要expire一个片段可以用expire_fragment方法,

expire_fragment(:controller => 'articles', :action => 'list')

2、Page cache和Action cache

class Article < AR::Base
  before_filter :authenticate, :only => [:edit, :create]
  caches_page :index
  caches_action :edit

  def index;end
  def edit;end
  def create
    expire_page :action => 'index'
    expire_action :action => 'edit'
  end
end

貌似caches_action和caches_page的区别也就只是action需要request经过一次rails stack,那也就能给要cache的action加点filter了;而page的话就完全不会经过rails了,直接就访问静态缓存的文件去了。。。

3、使用Sweeper管理cache

Swepper就是一个Observer,会在监视到一个特定的Model发生某些特定的callback时执行特定的活动,比如expire缓存

class ArticleSweeper < AC::Caching::Sweeper
  observe Article
  def after_create
    expire_page :controller => 'articles', :action => 'index'
    expire_action :controller =>' articles', :action => 'edit'
    expire_fragment :controller => 'articles', :action => 'list'
  end
end

4、浏览器端的缓存

通过stale?方法来判断Headers中的etag和last_modifiled是否与server上的一致

def show
  @article = Article.find(params[:id])
  if stale?(:etag => @article, :last_modifiled=>@article.updated_at.utc)
    respond_to do |format|
    end
   end
end

然后就是memcache相关的了,有时间再记录

 

 

cache and sweeper in rails

 

以关键字来分组显示列表,往往因为查询的数据量大,导致页面打开慢。使用cache action可以大大提高速度。

需要通过sweeper监控model的增删改动作,定时清理缓存。

models:

 

 

class TagPage < Page
  has_one :extra, :class_name => "TagPageExtra", :autosave => true
end
 
class TagPageExtra < ActiveRecord::Base
  belongs_to :tag_page
end

TagPage是扩展了以前的代码,对于新增的属性,通过TagPageExtra来处理。

controller:

class TagPagesController < ApplicationController
  caches_action :list
 
  def list
    @pages = TagPage.all(:select => "pages.id, pages.title, pages.description", :include => [:extra],
: order => "tag_page_extras.position")
  end
 
end
sweeper:
#文件放在 app/sweepers文件夹下
#并在environment中添加 文件路径
onfig.load_paths += %W( #{RAILS_ROOT}/app/sweepers )
 
class TagPageSweeper < ActionController::Caching::Sweeper
  observe TagPage
 
   def after_create(obj)
    expire_cache_for(obj)
  end
 
  def after_destroy(obj)
    expire_cache_for(obj)
  end
 
  def after_update(obj)
    expire_cache_for(obj)
  end
 
  private
  def expire_cache_for(obj)
    expire_action(list_tag_pages_path)
  end
end
 
#注意要在application controller中声明 才会起作用
class ApplicationController < ActionController::Base
  cache_sweeper :tag_page_sweeper
end

view:

 

<%= t("records_not_found")  if @pages.blank? %>
<dl>
  <% @pages.group_by(&:description).each do |description, pages| %>
  <dt><%= link_to description.nil? ? "<b>null</b>" : "<b>#{description}</b>", tag_pages_path(:tag => description) %></dt>
  <dd>
    <ul>
    <% pages.each do |page| %>
      <li class="tag"><%= link_to page.title, edit_tag_page_path(page) %></li>
    <% end %>
    </ul>
  </dd>
  <% end %>
</dl>
 

结果:
ProcessingTagPagesController#list (for xxx.xxx.xxx.xxx at 2010-07-08 09:24:03) [GET]
Filter chain halted as [#<ActionController::Filters::AroundFilter:0xb60d27d8 @identifier=nil,
@kind=:filter, @method=#<Proc:0xb79b6ba0@/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.4/lib/action_controller/caching/actions.rb:64>,
@options={:only=>#<Set: {"list"}>, :if=>nil, :unless=>nil}>] did_not_yield.
Completed in 8ms (View: 0, DB: 0) | 200 OK [http://www.xxxx.com/tag_pages/list]
这次直接读取了缓存 DB查询为0.

item_list

 
****************************

来自IBM 的文章:

真实世界中的 Rails: 

rails中的缓存    高级页面缓存    优化ActiveRecord

这个文章超好,尤其是第一篇**************

×××××××××××××××××

 

Rails缓存总览:Caching with Rails: An overview
 
大家都会用到缓存。本指南将会告诉你如何减少数据库的访问次数并且在很短时间内向用户提供他们要的东西。
 
缓存基础 Basic Caching
 
这是一个不适用任何第三方插件的关于rails自带的3种类型的缓存技术的介绍。
 
在我们开始之前,确保 config/enviroments/*.rb 中的development.rb 中 config.action_controller.perform_caching 被设置为True,默认状态下, 开发环境和测试环境这行都被注释掉的,只有生产环境中是默认被打开的。
 
Ruby代码 
config.action_controller.perform_caching = true  
 
1.1 页面缓存 Page Caching
 
页面缓存是Rails机制,允许只通过web服务器调用一个已经实际生成的页面,而不需要通过rails堆栈来呈现给用户,速度超级快。不幸的是,他不能提供很多情况下的支持,比如一个需要验证用户身份的页面的情况。并且在页面缓存中,web服务器直接调用文件系统种的一个页面缓存文件,因此还需要建立缓存失效策略,比如你更新了应该显示的页面内容等等。
 
那么,你要如何来使用这个超级快的缓存呢?简单,假设你有一个叫ProductsController的控制器 和 一个会列出所有商品的叫 list 的action。
 
Ruby代码 
class ProductsController < ActionController  
  caches_page :index  
  
  def index  
  end  
end  
 
当products/index第一次被访问的时候,Rails会创建一个名为index.html的文件,在那以后,web服务器会在下次收到products/index访问请求的时候直接调用那个静态文件。
 
默认情况下,页面缓存的文件夹在rails根目录的 public文件夹下。这个可以通过改变配置
 
Ruby代码 
setting config.action_controller.page_cache_directory  
 
来设置。修改默认的/public存储位置有利于在你可能想他别的静态文件放在public中的时候避免文件名的命名冲突。修改这个设置的同时,还需要配置你的web服务器,否则他会找不到你的缓存文件。
 
页面缓存机制会为没有扩展名的页面请求自动的添加.html扩展名,来让web服务器很容易的找到这些页面。这个也可以通过改变配置文件来进行设置:
 
Ruby代码 
config.action_controller.page_cache_extension  
 
为了在新添加商品后,让页面缓存失效,你可以像这样来写你的products控制器:
 
Ruby代码 
class ProductsController < ActionController  
  
  caches_page :index  
  
  def index; end  
  
  def create  
    expire_page :action => :index  
  end  
  
end  
 
如果你想要一个更复杂的缓存失效计划,你可以使用缓存清道夫(sweepers)来让当缓存原来的对象改变时让缓存过期。这个在在Sweepers那节有讲述。
 
注意:页面缓存会无视所有的参数,所以 /products/list?page=1 在缓存文件系统中生成的文件是 /products/list.html。如果有人访问/proucts/list?page=2 ,那么他得到的结果和 page=1 是一样的。所以你应当注意页面缓存中URL使用GET参数的情况。
 
1.2 动作缓存 Action Caching
 
不能使用页面缓存的情况之一就是你不能将其用于那些需要身份验证的页面。这个时候你就可以使用动作缓存。动作缓存的工作原理和页面缓存差不多,只是 web请求会从web服务器传递给Rails解析器和Action Pack,这样以来过滤器就可以在缓存被调用以前生效。这样你就可以使用身份验证和别的一些限制,同时输出缓存副本。
 
清除Action缓存和页面缓存的方式完全相同。
 
比方说,你只是想验证用户编辑或者创建一个产品,但是仍然缓存这些页面:
 
Ruby代码 
class ProductsController < ActionController  
  
  before_filter :authenticate, :only => [ :edit, :create ]  
  caches_page :index  
  caches_action :edit  
  
  def index; end  
  
  def create  
    expire_page :action => :index  
    expire_action :action => :edit  
  end  
  
  def edit; end  
  
end  
 
你还可以使用 :if(或者 :unless) 来传递一个 指定何时应缓存 action 的 Proc。此外,你还可以使用 :layout=> false 使缓存没有布局,这样以来布局模板中的动态信息比如已经登录的用户的用户名或者是购物车中商品的数量可以被保留。使用这项功能需要Rails2.2及更高 版本。
 
你可以传递一个:cache_option选项来修改默认的缓存路径。这会直接传递给 ActionCachePath.path_for 。这对于有多个可用路由并且需要做不同缓存的情况非常有用。如果指定了一个代码块,那么他将被当前控制器实例所调用。
 
最后,如果你在使用 memcache,你还可以传递 :expires_in 。事实上,所以 caches_action 没用到的参数都会被发送给底层缓存存储。
 
1.3 片断缓存 Fragment Caching
 
如果我们只需要把一个页面或者一个action的内容缓存起来然后发给用户那该多好。不幸的是,动态web应用通常都由很多个部份组成,他们具有不 同的页面特征。为了解决此类的动态创建的页面,在页面的不同部份需要建立不同的缓存和缓存失效机制。Rails提供了一种叫片断缓存的东西来解决这个问 题。
 
片断缓存允许视图逻辑的一个被缓存块包裹起来的片断形成一个缓存,在下次受到请求的时候发送出去。
 
举个例子,如果你想即时的显示你的网站中的全部订单并且不想在那个部份使用缓存,但是想缓存页面上列出全部商品的列表,你可以使用这段代码:
 
Ruby代码 
<% Order.find_recent.each do |o| %>  
  <%= o.buyer.name %> bought <% o.product.name %>  
<% end %>  
  
<% cache do %>  
  All available products:  
  <% Product.find(:all).each do |p| %>  
    <%= link_to p.name, product_url(p) %>  
  <% end %>  
<% end %>  
 
我们示例中的缓存块将绑定到调用他的那个action并且和acion缓存放在相同的路径下。这意味着如果你的每个action中有多个缓存片断,你就应当为cache调用提供一个 action_suffix
 
Ruby代码 
<% cache(:action => 'recent', :action_suffix => 'all_prods') do %>  
  All available products:  
<% Product.find(:all).each do |p| %>  
    <%= link_to p.name, product_url(p) %>  
  <% end %>  
<% end %>  
你可以使用expire_fragment 来使缓存过期:
 
Ruby代码 
expire_fragment(:controller => 'products', :action => 'recent',   :action_suffix => 'all_prods  
 
如果你不希望缓存块绑定到调用它的那个action,也可以使用全局标示片段。像这样,通过一个键来调用缓存方法:
 
Ruby代码 
<% cache(:key => ['all_available_products', @latest_product.created_at].join(':')) do %>  
  All available products:  
<% end %>  
 
这个片段就可以在Products控制器的所有actions中使用键来调用并且使用相同的方法来使之失效:
 
Ruby代码 
expire_fragment(:key => ['all_available_products', @latest_product.created_at].join(':'))  
 
1.4  扫地大妈 Sweepers
 
缓存扫地大妈 是一种允许你把代码中的一大堆 expire_{page,action.fragment}调用放在一起的机制。这是通过把所有的清除缓存内容的工作都移动到 ActionController::Cacheing::Sweeper 类种来实现的。这个类是一个通过回调监控一个对象状态改变的监视器。当发生改变的时候,他就通过那个对象的前后过滤器或后置过滤器来让那个对象的缓存失 效。
 
继续我们的Product控制器示例,我们可以像这样通过sweeper来重写他:
 
Ruby代码 
class StoreSweeper < ActionController::Caching::Sweeper  
  # This sweeper is going to keep an eye on the Product model  
  observe Product  
  
  # If our sweeper detects that a Product was created call this  
  def after_create(product)  
          expire_cache_for(product)  
  end  
  
  # If our sweeper detects that a Product was updated call this  
  def after_update(product)  
          expire_cache_for(product)  
  end  
  
  # If our sweeper detects that a Product was deleted call this  
  def after_destroy(product)  
          expire_cache_for(product)  
  end  
  
  private  
  def expire_cache_for(record)  
    # Expire the list page now that we added a new product  
    expire_page(:controller => '#{record}', :action => 'list')  
  
    # Expire a fragment  
    expire_fragment(:controller => '#{record}',   
      :action => 'recent', :action_suffix => 'all_products')  
  end  
end  
 
The sweeper has to be added to the controller that will use it. So, if we wanted to expire the cached content for the list and edit actions when the create action was called, we could do the following:
 
扫地大妈已经被添加到那些会用到他的控制器中。所以,如果我们想要在create action 被调用的时候,清除list 和 edit 两个action的缓存内容,我们可以这样做:
 
Ruby代码 
class ProductsController < ActionController  
  
  before_filter :authenticate, :only => [ :edit, :create ]  
  caches_page :list  
  caches_action :edit  
  cache_sweeper :store_sweeper, :only => [ :create ]  
  
  def list; end  
  
  def create  
    expire_page :action => :list  
    expire_action :action => :edit  
  end  
  
  def edit; end  
  
end  
 
1.5 SQL缓存   SQL Caching
 
查询缓存是一个对每个查询返回的数据集进行缓存的一个Rails特色。如果Rails在本次请求种再次发起了相同的查询,他就会使用被缓存起来的结果集而不需要再次对数据库发出请求。
 
Ruby代码 
class ProductsController < ActionController  
  
  before_filter :authenticate, :only => [ :edit, :create ]  
  caches_page :list  
  caches_action :edit  
  cache_sweeper :store_sweeper, :only => [ :create ]  
  
  def list  
    # Run a find query  
    Product.find(:all)  
  
    ...  
  
    # Run the same query again  
    Product.find(:all)  
  end  
  
  def create  
    expire_page :action => :list  
    expire_action :action => :edit  
  end  
  
  def edit; end  
  
end  
 
在上面的list 这个action,Product.find(:all)返回的结果会被缓存起来,下次再发起这个finder调用的时候就不需要再次读取数据库了。
 
1.6  缓存存储 Cache Stores
 
Rails 提供为action 缓存 和 片段缓存的数据提供很不同的存储方法。页面缓存通常是被存在磁盘上的。
 
Rails2.1 以及更新版本提供了可以缓存字符串的 ActiveSupport::Cache::Store 。一些像MemoryStore的缓存存储可以缓存任意的Ruby对象,但不要指望每个缓存存储都可以这样做。
 
钢轨 2.1 和以上提供 ActiveSupport::Cache::Store,可用于缓存的字符串。 一些缓存存储像 MemoryStore 的实现,能够缓存任意的 Ruby 对象,但不要指望能够这样做的每个缓存存储区。
 
默认的Rails包含的缓存存储提供了:
 
1) ActiveSupport::Cache::MemoryStore:一种用相同进程把所有的东西存储到内存中的缓存存储实现。如果你运行了多个 Ruby on Rails服务进程(比如 你在同时使用 mongrel_cluser 或者 Phusion Passenger),然后这意味着你的Rails服务进程实例不能够彼此共享缓存数据。如果你的应用不执行手动的缓存失效(如比你用缓存键),然后使用 MemoryStore是可以的。否则,仔细考虑是否您应该使用此缓存存储。
 
MemoryStoreis 不仅仅可以存储字符串,而且也可以存储任意Ruby对象。MemoryStoreis不是安全线程。如果你需要安全线程,请改用SynchronizedMemoryStore 的内容。
 
Ruby代码 
ActionController::Base.cache_store = :memory_store  
 
2) ActiveSupport::Cache::FileStore:缓存数据存放在磁盘上。这个默认的存储,路径是/tmp/cache. 在所有的环境下都工作得很好,并且允许所有的线程从相同应用程序文件夹中访问缓存内容。如果/tmp/cache不存在,那么默认的存储方式变成 memoryStore.
 
Ruby代码 
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"  
 
3) ActiveSupport::Cache::DRbStore: 缓存的数据存储在一个所有服务可以与之通信的单独的共享DRb进程中。这适用于所有环境,并为所有进程保持一个缓存。但是这就要求你运行并管理一个单独的DRb线程。
 
Ruby代码 
ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"  
 
4) MemCached store: Works like DRbStore, but uses Danga’s MemCache instead. Rails uses the bundled memcached-client gem by default. This is currently the most popular cache store for production websites.
 
4) MemCached store:
 
未完---待续
 

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter
Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee