1. 介绍

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

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 HTML Cache digest for app/views/home/index.html.slim: 8e89c7a7d1da1d9719fca4639859b19d Read 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 HTML
Cache 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: 1c628fa3d96abde48627f8a6ef319c1c

Read 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 < ApplicationController
def 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)。

完结

redis实现cache系统实践(六)的更多相关文章

  1. redis实现cache系统原理(五)

    1. 介绍 cache就是人们所说的缓存.我们这里所说的cache是web上的.对用户来说,衡量一个网站是否具有良好的体验,其中一个标准就是响应速度的快慢.可能网站刚上线,功能还较少,数据库的记录也不 ...

  2. 搭建一个redis高可用系统

    一.单个实例 当系统中只有一台redis运行时,一旦该redis挂了,会导致整个系统无法运行. 单个实例 二.备份 由于单台redis出现单点故障,就会导致整个系统不可用,所以想到的办法自然就是备份( ...

  3. Couchbase介绍,更好的Cache系统

    在移动互联网时代,我们面对的是更多的客户端,更低的请求延迟,这当然需要对数据做大量的 Cache 以提高读写速度. 术语 节点:指集群里的一台服务器. 现有 Cache 系统的特点 目前业界使用得最多 ...

  4. Couchbase 介绍 - 更好的 Cache 系统

    在移动互联网时代,我们面对的是更多的客户端,更低的请求延迟,这当然需要对数据做大量的 Cache 以提高读写速度. 术语 节点:指集群里的一台服务器. 现有 Cache 系统的特点 目前业界使用得最多 ...

  5. (转)Couchbase介绍,更好的Cache系统

    在移动互联网时代,我们面对的是更多的客户端,更低的请求延迟,这当然需要对数据做大量的 Cache 以提高读写速度. 术语 节点:指集群里的一台服务器. 现有 Cache 系统的特点 目前业界使用得最多 ...

  6. 基于nginx+lua+redis高性能api应用实践

    基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...

  7. [原创]ubuntu14.04部署ELK+redis日志分析系统

    ubuntu14.04部署ELK+redis日志分析系统 [环境] host1:172.17.0.4 搭建ELK+redis服务 host2:172.17.0.3 搭建logstash+nginx服务 ...

  8. 转 -Filebeat + Redis 管理 LOG日志实践

    Filebeat + Redis 管理 LOG日志实践 小赵营 关注 2019.01.06 17:52* 字数 1648 阅读 24评论 0喜欢 2 引用 转载 请注明出处 某早上,领导怒吼声远远传来 ...

  9. 用POLARDB构建客到智能餐饮系统实践

    在新零售成为大趋势的今天,餐饮行业也加入到这一浪潮之中.智能餐饮系统将帮助餐饮行业从多个维度提升自己的运营能力和收益,而打造智能餐饮系统SaaS化能力也成为了目前的一个热点.本文中果仁软件联合创始人& ...

随机推荐

  1. 你所不知道的JSON

    译者按: 老司机们,你知道JSON.stringify还有第二个和第三个可选参数吗?它们是什么呢? JSON已经逐渐替代XML被全世界的开发者广泛使用.本文深入讲解JavaScript中使用JSON. ...

  2. SAN和NAS的区别: 层次不一样

    SAN : STORAGE AREA NETWORK   存储区域网络 NAS : NETWORK ATTACHED STORAGE 网络附加存储 NAS不一定是盘阵,一台普通的主机就可以做出NAS, ...

  3. android monkey app乱点测试

    Monkey是Android中的一个命令行工具 查看包名:查看电脑中某一位置的apk文件的包名:PC打开CMD-进入TMG目录-运行设备--查看包名aapt dump badging *.apk(ap ...

  4. PHP多进程(4) :内部多进程

    说的都是只兼容unix 服务器的多进程,下面来讲讲在window 和 unix 都兼容的多进程(这里是泛指,下面的curl实际上是通过IO复用实现的). 通过扩展实现多线程的典型例子是CURL,CUR ...

  5. 无偏估计(Unbiased Estimator)

    无偏估计是参数的样本估计量的期望值等于参数的真实值. 一个简单的例子(https://www.zhihu.com/question/22983179/answer/23470969): 比如我要对某个 ...

  6. 解决cookie跨域访问.2

    v一.前言 随着项目模块越来越多,很多模块现在都是独立部署.模块之间的交流有时可能会通过cookie来完成.比如说门户和应用,分别部署在不同的机器或者web容器中,假如用户登陆之后会在浏览器客户端写入 ...

  7. hdu 1813(IDA*)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1813 思路:首先bfs预处理出‘0’到边界点最短距离,然后构造 h() 为所’0‘点逃离迷宫的最少步数 ...

  8. python3----datetime模块分析

    datetime模块用于是date和time模块的合集,datetime有两个常量,MAXYEAR和MINYEAR,分别是9999和1. datetime模块定义了5个类,分别是 1.datetime ...

  9. 剑指 offer set 24 扑克牌的顺子

    题目 从扑克牌中任意抽取出 5 张牌, 判断是不是顺子, 并且大小王可以看成任意的数字 思路 1. 把大小王当做 0 插入到数组中, 然后对数组排序 2. 统计相邻两个数之间的空隙数, 若空隙数大于 ...

  10. open() 函数以 a+ 模式打开文件

    这种模式打开文件,可读可写,从文件顶部读取内容,从文件底部追加内容,文件不存在则自动创建 [root@localhost ~]$ cat 1.txt aaa bbb ccc In [1]: data ...