redis实现cache系统实践(六)

1. 介绍

rails中就自带有cache功能,不过它默认是用文件来存储数据的。我们要改为使用redis来存储。而且我们也需要把sessions也存放到redis中。关于rails实现cache功能的源码可见于这几处:

  • https://github.com/rails/rails/blob/master/activesupport/lib/active_support/cache.rb
  • https://github.com/rails/rails/tree/master/activesupport/lib/active_support/cache
  • https://github.com/rails/rails/blob/master/actionview/lib/action_view/helpers/cache_helper.rb

2. 使用

我们一步步在rails中使用cache实现我们的需求。

2.1 开启cache模式

首先第一步我们要来开启cache模式。默认情况下,production环境是开启的,但是development没有,所以要开启它。

进入config/environments/development.rb文件中,把config.action_controller.perform_caching设为true。

config.action_controller.perform_caching = true 

修改完,记得重启服务器。

2.2 使用html片断cache

为了方便测试和了解整个原理,我们先不使用redis来存放cache数据,只使用默认的文件来存放数据。

以本站为例,我们要把首页的"最近的文章"那部分加上html片断的cache。

使用html片断cache,rails提供了一个helper方法可以办到,很简单,只需要把需要的html用cache包起来。

.row- cache do .col-md-6 .panel.panel-default .panel-heading div 最近的文章 .panel-body - @articles.each do |article| p.clearfix span.pull-right = datetime article.created_at = link_to article.title, article_path(article) 

我们先在页面刷新一下,然后通过日志来观察。

先发现访问起来比平时慢一点点,因为它在把cache存到文件中,具体的log是下面这样的。

Started GET "/" for 127.0.0.1 at 2015-10-30 16:19:27 +0800
Processing by HomeController#index as HTMLCache digest for app/views/home/index.html.slim: 8e89c7a7d1da1d9719fca4639859b19dRead fragment views/localhost:4000//8e89c7a7d1da1d9719fca4639859b19d (0.3ms)Article Load (2.0ms)  SELECT  "articles"."title", "articles"."created_at", "articles"."published", "articles"."group_id", "articles"."slug", "articles"."id" FROM "articles" WHERE "articles"."published" = $1  ORDER BY id DESC LIMIT 10  [["published", "t"]]Group Load (3.5ms)  SELECT "groups".* FROM "groups" WHERE "groups"."id" IN (1, 5, 3, 4)Article Load (0.9ms)  SELECT  "articles"."title", "articles"."created_at", "articles"."published", "articles"."group_id", "articles"."slug", "articles"."id", "articles"."visit_count" FROM "articles" WHERE "articles"."published" = $1  ORDER BY visit_count DESC LIMIT 10  [["published", "t"]]Group Load (2.3ms)  SELECT "groups".* FROM "groups" WHERE "groups"."id" IN (1, 3, 4)Group Load (4.4ms)  SELECT "groups".* FROM "groups"Write fragment views/localhost:4000//8e89c7a7d1da1d9719fca4639859b19d (41.7ms)
...

主要就是Cache digestRead fragmentWrite fragment部分。

Cache digest是产生一个md5码,这个码来标识html的片断,会很有用,我们等下再来细说。

Read fragment是读取html片断(以文件形式存储),根据之前产生的md5标识,发现不存在,就会生成一个html片断并存起来,就是Write fragment

默认情况下,产生的html片断文件是存在/tmp/cache目录里的,如下:

~/codes/rails365 (master) $ tree tmp/cache/
tmp/cache/
├── 20B
│   └── 6F1
│       └── views%2Flocalhost%3A4000%2F%2F8e89c7a7d1da1d9719fca4639859b19d

打开views%2Flocalhost%3A4000%2F%2F8e89c7a7d1da1d9719fca4639859b19d这个文件,就会发现里面存储的就是html的片断。

现在我们在刷新一遍页面,再来看看日志。

Started GET "/" for 127.0.0.1 at 2015-10-30 16:53:18 +0800
Processing by HomeController#index as HTMLCache digest for app/views/home/index.html.slim: 8e89c7a7d1da1d9719fca4639859b19d
Read fragment views/localhost:4000//8e89c7a7d1da1d9719fca4639859b19d (0.3ms)
...

就会发现Write fragment没有了,也不查询数据库了,数据都从html片断cache取了。

这样还不算完成。我们要考虑一个问题,就是我们改了数据或html的内容的时候,cache会自动更新吗?

2.3 Cache digest

先来说更改html片断代码本身的情况。

我们把"最近的文章"改成”最新的文章",然后我们来观察是否会生效。

最终通过查看日志,发现还是产生了Write fragment,说明是生效的。

这个原理是什么呢?

我们找到cache这个helper方法的源码。

def cache(name = {}, options = {}, &block) if controller.respond_to?(:perform_caching) && controller.perform_caching safe_concat(fragment_for(cache_fragment_name(name, options), options, &block)) else yield end nil end 

发现关键在cache_fragment_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 def fragment_name_with_digest(name, virtual_path) #:nodoc: virtual_path ||= @virtual_path if virtual_path name = controller.url_for(name).split("://").last if name.is_a?(Hash) digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies [ name, digest ] else name end end 

关键就在fragment_name_with_digest这个方法里,它会找到cache的第一个参数,然后用这个参数的内容生成md5码,我们刚才改变了html的内容,也就是参数改变了,md5自然就变了,md5一变就得重新生成html片断。

所以cache方法的第一个参数是关键,它的内容是判断重不重新产生html片断的依据。

改变html片断代码之后,是会重新生成html片断的,但如果是在articles中增加一条记录呢?通过尝试发现不会重新生成html片断的。

那我把@artilces作为第一个参数传给cache方法。

.row- cache @articles do .col-md-6 .panel.panel-default .panel-heading div 最近的文章 .panel-body - @articles.each do |article| p.clearfix span.pull-right = datetime article.created_at = link_to article.title, article_path(article) 

发现生成了Write fragment,说明是可以的,页面也会生效。

Cache digest for app/views/home/index.html.slim: 1c628fa3d96abde48627f8a6ef319c1cRead fragment views/articles/15-20151027051837664089000/articles/14-20151030092311065810000/articles/13-20150929153356076334000/articles/12-20150929144255631082000/articles/11-20151027064325237540000/articles/10-20150929153421707840000/articles/9-20150929123736371074000/articles/8-20150929144346413579000/articles/7-20150929144324012954000/articles/6-20150929144359736164000/1c628fa3d96abde48627f8a6ef319c1c (0.1ms)Write fragment views/articles/15-20151027051837664089000/articles/14-20151030092311065810000/articles/13-20150929153356076334000/articles/12-20150929144255631082000/articles/11-20151027064325237540000/articles/10-20150929153421707840000/articles/9-20150929123736371074000/articles/8-20150929144346413579000/articles/7-20150929144324012954000/articles/6-20150929144359736164000/1c628fa3d96abde48627f8a6ef319c1c (75.9ms)Article Load (2.6ms)  SELECT  "articles"."title", "articles"."created_at", "articles"."updated_at", "articles"."published", "articles"."group_id", "articles"."slug", "articles"."id", "articles"."visit_count" FROM "articles" WHERE "articles"."published" = $1  ORDER BY visit_count DESC LIMIT 10  [["published", "t"]]

但是,除此之外,还有sql语句生成,而且以后的每次请求都有,即使命中了cache,因为@articles作为第一个参数,它的内容是要通过数据库来查找的。

那有一个解决方案是这样的:把@articles的内容也放到cache中,这样就不用每次都查找数据库了,而一旦有update或create数据的时候,就让@articles过期或者重新生成。

为了方便测试,我们先把cache的存储方式改为用redis来存储数据。

添加下面两行到Gemfile文件,执行bundle

gem 'redis-namespace'
gem 'redis-rails'

config/application.rb中添加下面这一行。

config.cache_store = :redis_store, {host: '127.0.0.1', port: 6379, namespace: "rails365"} 

@articles的内容要改为从redis获得,主要是读redis中健为articles的值。

class HomeController < ApplicationControllerdef index @articles = Rails.cache.fetch "articles" do Article.except_body_with_default.order("id DESC").limit(10).to_a end end end 

创建或生成一条article记录,都要让redis的数据无效。

class Admin::ArticlesController < Admin::BaseController ... def create @article = Article.new(article_params) Rails.cache.delete "articles" ... end end 

这样再刷新两次以上,就会发现不再查数据库了,除非添加或修改了文章(article)。

完结

 

转载于:https://www.cnblogs.com/like-minded/p/5239440.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/404240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Redux/Mobx] 在redux中,什么是action?

[Redux/Mobx] 在redux中&#xff0c;什么是action&#xff1f; action是Redux中定义一个响应的动作&#xff0c;action总是有一个type属性&#xff0c;作为这个动作的唯一标识; Reducer函数则会根据这个action.type来如何生成并返回一个新的state 个人简介 我是歌谣&#x…

VMware 9 安装 Mac OS X 10.8 Mountain Lion 图文全程

From: http://unmi.cc/vmware9-install-mac-os-x-mountain-lion/#comment-8684 本教程是在 VMware 9 下安装当前最新版的 Mac OS X Mountain Lion 苹果系统。曾在 VirtualBox/VMware 下安装过 Mac OS Lion 系统&#xff0c;但安装后是无法升级到 Mountain Lion 的&#xff0c;所…

显示和快速隐藏Mac桌面所有图标

隐藏&#xff1a;defaults write com.apple.finder CreateDesktop -bool false;killall Finder;say icons hidden恢复显示&#xff1a;defaults write com.apple.finder CreateDesktop -bool true;killall Finder;say icons show上面的操作甚至可以制作成为一个Applescript&…

正则表达式基础恶补

1.基本用途 正则表达式两种基本用途&#xff1a;搜索和替换。 2.单个字符匹配(.).字符可以匹配任何单个字符&#xff0c;类似于sql中的_。注意特殊字符匹配时要使用反斜杠\\。 3.匹配一组字符([])[]之间的所有字符都是该集合的组成部分&#xff0c;字符集合的匹配结果是能够与集…

[Redux/Mobx] Mobx和Redux有什么区别?

[Redux/Mobx] Mobx和Redux有什么区别&#xff1f; Redux每一次的dispatch都会从根reducer到子reducer嵌套递归的执行&#xff0c;所以效率相对较低&#xff1b;而Mobx的内部使用的是依赖收集&#xff0c;所以不会有这个问题&#xff0c;执行的代码较少&#xff0c;性能相对更高…

ANSIC标准定义的6种预定义宏

From: http://biancheng.dnbcw.info/c/277439.html ANSIC标准定义了以下6种可供C语言使用的预定义宏&#xff1a; __LINE__ 在源代码中插入当前源代码行号 __FILE__ 在源代码中插入当前源代码文件名 …

初识Vulkan

Vulkan是Khronos组织制定的“下一代”开放的图形显示API&#xff0c;是与DirectX12可以匹敌的GPU API标准。Vulkan是基于AMD的Mantle API演化而来&#xff0c;目前Vulkan 1.0标准已经完成并正式发布。上一代的OpenGL|ES并不会被遗弃&#xff0c;还会继续发展&#xff0c;很有可…

[Redux/Mobx] 说说Redux的实现流程

[Redux/Mobx] 说说Redux的实现流程 通过分析redux的几个核心api&#xff0c;来看如何实现redux store.subscribe: 订阅数据的变化store.dispatch&#xff1a;dispatch后改变state,同时通知store.subscribe函数执行&#xff0c;上面这两个函数可以利用发布-订阅模式store.getS…

数据镜像备份工具rsync

rsync可以用来实现数据本地镜像和远程备份。 1&#xff0c;可以镜像保存整个目录和文件。 2&#xff0c;可以增量同步数据&#xff0c;文件传输率高。 3&#xff0c;可以保持原文件的权限&#xff0c;时间等属性。 4&#xff0c;可以加密传输数据。 5&#xff0c;可以使用rcp、…

Error format not a string literal and no format arguments解决方案

From: http://www.cnblogs.com/hhuang2012/p/3336911.html 场景: cocos2dx 跨平台开发, 移植Android版本时, 当进行到build_native.sh步骤后 ndk版本: android-ndk-r9 开发环境: mac os 64bit, AndroidEclipse BUG: CCCommon.cpp:54:77: error: format not a string literal an…

[Redux/Mobx] redux的数据存储和本地储存有什么区别?

[Redux/Mobx] redux的数据存储和本地储存有什么区别&#xff1f; Redux存储的数据本质上都是JS变量&#xff0c;都是在内存中的&#xff0c;页面刷新就会消失本质存储是像localStorage Cookie IndexDB WebSQL等缓存技术&#xff0c;它是存储在硬盘中的&#xff0c;不会随便页面…

java_IO流之 NIO

NIO 定义 即新IO&#xff0c;在JDK1.4的java.nio.*包中引入&#xff0c;其目的在于提高速度。 在Java1.4之前的I/O系统中&#xff0c;提供的都是面向流的I/O系统&#xff0c;系统一次一个字节地处理数据&#xff0c;一个输入流产生一个字节的数据&#xff0c;一个输出流消费一个…

iOS iPhone SDK 包含哪些东西?

From&#xff1a; http://www.cnblogs.com/jy578154186/archive/2013/02/27/2934881.html iPhone SDK 包含哪些东西&#xff1f; 第一部分&#xff1a; 在使用Intel芯片的Macintosh计算机开发iOS应用程序所需的全部接口、工具以及资源全都包含于iPhone SDK。 苹果公司将大部分系…

[Redux/Mobx] 你有使用过redux-saga中间件吗?它是干什么的?

[Redux/Mobx] 你有使用过redux-saga中间件吗&#xff1f;它是干什么的&#xff1f; redux-saga 就是用来处理副作用&#xff08;例如&#xff1a;AJAX请求&#xff09;的一个中间件&#xff0c;使用Generator 函数 个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知…

python Pillow 的简单案例

&#xff03;代码来自 实验楼https://www.shiyanlou.com/courses/ #encodingutf-8#/usr/bin/env pythonfrom PIL import Imageimport argparse parser argparse.ArgumentParser() parser.add_argument(file)parser.add_argument(-o , --output)parser.add_argument(--width, t…

“我的电脑”右键“管理”打不开,提示“该文件没有与之关联的程序来执行该操作“

From: http://bbs.kafan.cn/thread-1240058-1-1.html 如图&#xff1a; 右键单击桌面上的“我的电脑”&#xff0c;再点“管理”就弹出这个对话框了。 【该文件没有与之关联的程序来执行该操作。请安装一个程序&#xff0c;或者&#xff0c;如果已安装程序&#xff0c;请在“…

[Redux/Mobx] Redux怎样重置状态?

[Redux/Mobx] Redux怎样重置状态&#xff1f; 先在store初始化时对store的初始化数据状态进行保存&#xff0c;然后reducer中多定义一个重置状态的type"RESET"的action的逻辑&#xff0c;这个操作里面直接返回初始的状态值&#xff0c;在需要重置状态的时候&#xf…

Java数据结构--HashTable(拉链法)

双向节点 /*** Created by root on 16-3-6.*/ public class Node<E> {public E data;public Node prev;public Node next;public Node(E target,Node prev,Node next){//链接两个孤立节点datatarget;this.prevprev;this.nextnext;}public Node(){this(null,null,null);}p…

[交流] 新手常见(五国)(-v图)错误解决(原版,破解kernel,补丁kext下载)

From: http://blog.csdn.net/ashuai81/article/details/8032952 原文出处&#xff1a;http://bbs.pcbeta.com/viewthread-863656-1-1.html 谢谢大神&#xff0c; 看了这个才弄出了黑苹果。 在可安装的主版硬件上&#xff0c;安装Mac不该是个大问题&#xff0c;声卡、显卡、…

[Redux/Mobx] Context api可以取代Redux吗?为什么?

[Redux/Mobx] Context api可以取代Redux吗&#xff1f;为什么&#xff1f; 可以&#xff0c;但是并不完美。 利用Context APIuseReducer的方案可以实现简化版的Redux。 Redux除了能够存储数据之外&#xff0c;它的强大之处还在于数据修改的单一性&#xff0c;清晰的数据流向&a…