原文:http://guides.rubyonrails.org/caching_with_rails.html
翻译: 蜗牛 (http://ywencn.iteye.com )
Rails缓存总览:Caching with Rails: An overview
大家都会用到缓存。本指南将会告诉你如何减少数据库的访问次数并且在很短时间内向用户提供他们要的东西。
缓存基础 Basic Caching
这是一个不适用任何第三方插件的关于rails自带的3种类型的缓存技术的介绍。
在我们开始之前,确保 config/enviroments/*.rb 中的development.rb 中 config.action_controller.perform_caching 被设置为True,默认状态下, 开发环境和测试环境这行都被注释掉的,只有生产环境中是默认被打开的。
config.action_controller.perform_caching = true
1.1 页面缓存 Page Caching
页面缓存是Rails机制,允许只通过web服务器调用一个已经实际生成的页面,而不需要通过rails堆栈来呈现给用户,速度超级快。不幸的是,他不能提供很多情况下的支持,比如一个需要验证用户身份的页面的情况。并且在页面缓存中,web服务器直接调用文件系统种的一个页面缓存文件,因此还需要建立缓存失效策略,比如你更新了应该显示的页面内容等等。
那么,你要如何来使用这个超级快的缓存呢?简单,假设你有一个叫ProductsController的控制器 和 一个会列出所有商品的叫 list 的action。
class ProductsController < ActionController caches_page :index def index end end
当products/index第一次被访问的时候,Rails会创建一个名为index.html的文件,在那以后,web服务器会在下次收到products/index访问请求的时候直接调用那个静态文件。
默认情况下,页面缓存的文件夹在rails根目录的 public文件夹下。这个可以通过改变配置
setting config.action_controller.page_cache_directory
来设置。修改默认的/public存储位置有利于在你可能想他别的静态文件放在public中的时候避免文件名的命名冲突。修改这个设置的同时,还需要配置你的web服务器,否则他会找不到你的缓存文件。
页面缓存机制会为没有扩展名的页面请求自动的添加.html扩展名,来让web服务器很容易的找到这些页面。这个也可以通过改变配置文件来进行设置:
config.action_controller.page_cache_extension
为了在新添加商品后,让页面缓存失效,你可以像这样来写你的products控制器:
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缓存和页面缓存的方式完全相同。
比方说,你只是想验证用户编辑或者创建一个产品,但是仍然缓存这些页面:
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提供了一种叫片断缓存的东西来解决这个问题。
片断缓存允许视图逻辑的一个被缓存块包裹起来的片断形成一个缓存,在下次受到请求的时候发送出去。
举个例子,如果你想即时的显示你的网站中的全部订单并且不想在那个部份使用缓存,但是想缓存页面上列出全部商品的列表,你可以使用这段代码:
<% 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
<% 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 来使缓存过期:
expire_fragment(:controller => 'products', :action => 'recent', :action_suffix => 'all_prods
如果你不希望缓存块绑定到调用它的那个action,也可以使用全局标示片段。像这样,通过一个键来调用缓存方法:
<% cache(:key => ['all_available_products', @latest_product.created_at].join(':')) do %> All available products: <% end %>
这个片段就可以在Products控制器的所有actions中使用键来调用并且使用相同的方法来使之失效:
expire_fragment(:key => ['all_available_products', @latest_product.created_at].join(':'))
1.4 扫地大妈 Sweepers
缓存扫地大妈 是一种允许你把代码中的一大堆 expire_{page,action.fragment}调用放在一起的机制。这是通过把所有的清除缓存内容的工作都移动到ActionController::Cacheing::Sweeper 类种来实现的。这个类是一个通过回调监控一个对象状态改变的监视器。当发生改变的时候,他就通过那个对象的前后过滤器或后置过滤器来让那个对象的缓存失效。
继续我们的Product控制器示例,我们可以像这样通过sweeper来重写他:
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的缓存内容,我们可以这样做:
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在本次请求种再次发起了相同的查询,他就会使用被缓存起来的结果集而不需要再次对数据库发出请求。
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 的内容。
ActionController::Base.cache_store = :memory_store
2) ActiveSupport::Cache::FileStore:缓存数据存放在磁盘上。这个默认的存储,路径是/tmp/cache. 在所有的环境下都工作得很好,并且允许所有的线程从相同应用程序文件夹中访问缓存内容。如果/tmp/cache不存在,那么默认的存储方式变成memoryStore.
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
3) ActiveSupport::Cache::DRbStore: 缓存的数据存储在一个所有服务可以与之通信的单独的共享DRb进程中。这适用于所有环境,并为所有进程保持一个缓存。但是这就要求你运行并管理一个单独的DRb线程。
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:
未完---待续