Rails源代码分析(18):ActionController::Caching(2) ActionCache

本文深入探讨了Rails应用中的缓存机制,特别是Action Cache的实现原理。通过示例展示了如何利用Action Cache进行权限验证,同时解析了其背后的实现机制,包括缓存路径的生成与过期策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 概述
cache机制分为5个Module实现
Pages, Actions, Fragments, Sweeping, SqlCache

2 使用
今天先看看ActionCache:
Action Cache和Page Cache的相同之处在于-整个输出被cache起来了,
但是两者的区别在于-Action Cache每个请求仍然会经过整个Action Pack,
这个cache的好处在于在访问页面之前,filter仍然会执行,这样可以判断权限之类的:
  1.        class ListsController < ApplicationController
  2.          before_filter :authenticate:except => :public
  3.          caches_page   :public
  4.          caches_action :index:show:feed
  5.        end
上面的例子中,public页面不需要权限判断,所以可以用page cache,
但是其他的方法,比如index show feed需要判断权限,所以可以作为action cache级别,

整个Action cache的实现采用了around filter来完成,
fragment cache的命名采用了host和path组合的模式,
http://david.somewhere.com/lists/show/1 fragment名字就是 "david.somewhere.com/lists/show/1"
这样就便于子域名的使用。
http://david.somewhere.com/lists和http://david.somewhere.com/lists.xml 是作为不同的request分开处理的。所以,记住使action cache :action => 'lists'过期和使:action => 'list', :format => :xml过期不同。

可以手动设置:cache_path来设置cache路径,也支持:if判断:
  1.        class ListsController < ApplicationController
  2.          before_filter :authenticate:except => :public
  3.          caches_page   :public
  4.          caches_action :index:if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request
  5.          caches_action :show:cache_path => { :project => 1 }
  6.          caches_action :feed:cache_path => Proc.new { |controller|
  7.            controller.params[:user_id] ?
  8.              controller.send(:user_list_url, c.params[:user_id], c.params[:id]) :
  9.              controller.send(:list_url, c.params[:id]) }
  10.        end

2 源代码分析
  1.       def self.included(base) #:nodoc:
  2.         base.extend(ClassMethods)
  3.           base.class_eval do
  4.             attr_accessor :rendered_action_cache:action_cache_path
  5.           end
  6.       end
上面入口可以看到
1 增加了ClassMethods提供的方法,
2 增加了rendered_action_cache 和 action_cache_path 两个变量用于在around_filter传递变量

  1.       module ClassMethods
  2.         # Declares that +actions+ should be cached.
  3.         # See ActionController::Caching::Actions for details.
  4.         def caches_action(*actions)
  5.           return unless cache_configured?
  6.           options = actions.extract_options!
  7.           # 给方法增加一个around_filter来进行cache操作
  8.           around_filter(ActionCacheFilter.new(:cache_path => options.delete(:cache_path)), {:only => actions}.merge(options))
  9.         end
  10.       end
  11.       protected
  12.         def expire_action(options = {})
  13.           return unless cache_configured?
  14.           if options[:action].is_a?(Array)
  15.             options[:action].dup.each do |action|
  16.               expire_fragment(ActionCachePath.path_for(self, options.merge({ :action => action })))
  17.             end
  18.           else
  19.             expire_fragment(ActionCachePath.path_for(self, options))
  20.           end
  21.         end

这个AroundFilter的实现ActionCacheFilter :

  1.       class ActionCacheFilter #:nodoc:
  2.         def initialize(options, &block)
  3.           @options = options
  4.         end
  5.         def before(controller)
  6.           # 获得cache_path
  7.           cache_path = ActionCachePath.new(controller, path_options_for(controller, @options))
  8.           if cache = controller.read_fragment(cache_path.path)
  9.             # 如果有cache那么设置 rendered_action_cache ,并且设置response content_type,直接返回cache文本
  10.             controller.rendered_action_cache true
  11.             set_content_type!(controller, cache_path.extension)
  12.             controller.send!(:render_for_text, cache)
  13.             false
  14.           else
  15.           # 如果没有cache那么action_cache_path设置为cache_path
  16.             controller.action_cache_path = cache_path
  17.           end
  18.         end
  19.         def after(controller)
  20.           # 如果存在cache并且允许cache
  21.           return if controller.rendered_action_cache || !caching_allowed(controller)
  22.           # 第一次,那么写cache
  23.           controller.write_fragment(controller.action_cache_path.path, controller.response.body)
  24.         end
  25.         private
  26.           def set_content_type!(controller, extension)
  27.             controller.response.content_type = Mime::Type.lookup_by_extension(extension).to_s if extension
  28.           end
  29.           def path_options_for(controller, options)
  30.             ((path_options = options[:cache_path]).respond_to?(:call) ? path_options.call(controller) : path_options) || {}
  31.           end
  32.           def caching_allowed(controller)
  33.             # 如果是get方法,如果是OK状态
  34.             controller.request.get? && controller.response.headers['Status'].to_i == 200
  35.           end
  36.       end
CachePath对象包装 : 
  1.       class ActionCachePath
  2.         attr_reader :path:extension
  3.         class << self
  4.           def path_for(controller, options)
  5.             new(controller, options).path
  6.           end
  7.         end
  8.         def initialize(controller, options = {})
  9.           @extension = extract_extension(controller.request.path)
  10.           path = controller.url_for(options).split('://').last
  11.           normalize!(path)
  12.           add_extension!(path, @extension)
  13.           @path = URI.unescape(path)
  14.         end
  15.         private
  16.           def normalize!(path)
  17.             path << 'index' if path[-1] == ?/
  18.           end
  19.           def add_extension!(path, extension)
  20.             path << ".#{extension}" if extension
  21.           end
  22.           def extract_extension(file_path)
  23.             # Don't want just what comes after the last '.' to accommodate multi part extensions
  24.             # such as tar.gz.
  25.             file_path[/^[^.]+/.(.+)$/, 1]
  26.           end
  27.       end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值