1. Introduction
Rails comes with a cache feature, but it uses files to store data by default. We're going to use Redis to store it instead. And we also need to store the sessions in Redis. The source code for the cache feature of rails is visible in these places:
- 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. Use
We use the cache in rails one step at a pace to meet our needs.
2.1 Turn on Cache mode
The first step is to start the cache mode. By default, the production environment is turned on, but development does not, so you have to turn it on.
Enter the config/environments/development.rb
file and config.action_controller.perform_caching
set to true.
config.action_controller.perform_caching = true
After modifying, remember to restart the server.
2.2 Using the HTML fragment cache
To facilitate testing and understanding of the whole principle, we will not use Redis to store the cache data, only the default file to hold the data.
Take this site as an example, we want to the homepage of the "recent article" that part plus the cache of HTML fragment.
Using HTML snippets Cache,rails provides a helper method that can be done simply by wrapping the required HTML with the cache.
.Row-CacheDo.Col-Md-6.Panel.Panel-Default.Panel-HeadingDiv recent article .-body - @articles .do | Article| p. Clearfix span. Pull-right = datetime article< Span class= "P". created_at = link_to article.< Span class= "NF" >titlearticle_path (article)
Let's refresh the page first and then look through the logs.
First find that access is a little slower than usual, because it is in the cache to the file, the specific log is the following.
Started GET "/" for 127.0.0.1 in 2015-10-30 16:19:27 +0800processing by Homecontroller#index as HTML Cache 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 "=" ORDER by ID DESC LIMIT [["Published", "T"]] Group Load (3.5ms) Select "Groups". * FROM "groups" wher E "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 "=" ORDER by Visit_count DESC LIMIT [["Published", "T"]] Group Load (2.3ms) Select "Groups". * FROM "Gro UPS "WHERE" groups "." ID "in (1, 3, 4) Group Load (4.4ms) Select "Groups". * FROM "groups" Write fragment views/localhost:4000//8e89c7a7d1da1d9719fca4639859b19d (41.7ms) ...
The main is Cache digest
, Read fragment
and Write fragment
part.
Cache digest
is to produce a MD5 code, this code to identify the fragment of the HTML, it will be very useful, we later to elaborate.
Read fragment
is to read the HTML fragment (stored as a file), according to the previously generated MD5 identity, found that does not exist, will generate an HTML fragment coexist together, that is Write fragment
.
By default, the resulting HTML fragment file is present in the/tmp/cache directory, as follows:
~/codes/rails365 (master) $ tree tmp/cache/tmp/cache/├── 20B│ └── 6F1│ └── views%2Flocalhost%3A4000%2F%2F8e89c7a7d1da1d9719fca4639859b19d
Open views%2Flocalhost%3A4000%2F%2F8e89c7a7d1da1d9719fca4639859b19d
This file and you will find that the HTML fragment is stored in it.
Now we're going to refresh the page again and look at the log.
Started GET "/" for 127.0.0.1 at 2015-10-30 16:53:18 +0800Processing by HomeController#index as HTML Cache digest for app/views/home/index.html.slim: 8e89c7a7d1da1d9719fca4639859b19dRead fragment views/localhost:4000//8e89c7a7d1da1d9719fca4639859b19d (0.3ms)...
Will find Write fragment
no, and do not query the database, the data are taken from the HTML fragment cache.
It's not finished yet. We have to consider a question, is that when we change the data or HTML content, the cache will automatically update it?
2.3 Cache Digest
First, change the HTML fragment code itself.
We changed the "recent article" to "the latest article" and then we looked into whether it would work.
Finally, by looking at the log, the discovery is still generated Write fragment
and the instructions are in effect.
What is this principle?
We found the source of the cache's helper method.
DefCache(Name={},Options={},&block) if controller respond_to? (:p erform_caching) && Controller.safe_concat (fragment_for ( cache_fragment_name (name), options&block< Span class= "P")) else yield end nil end
Find the key in cache_fragment_name
this method, the following two methods are found in compliance.
DefCache_fragment_name(Name={},Skip_digest:Nil,Virtual_path:Nil)IfSkip_digestNameElseFragment_name_with_digest(Name,Virtual_path)EndEndDefFragment_name_with_digest(Name,Virtual_path)#:nodoc:Virtual_path|| =@virtual_pathIfVirtual_pathName=Controller.Url_for(Name). split (if name. Is_a? (hash) digest = digestor. Digest name: virtual_pathfinder: lookup_contextdependencies: view_cache_ Dependencies [namedigest Span class= "K" >else name endend
The key is in fragment_name_with_digest
this method, it will find the first parameter of the cache, and then use the content of this parameter to generate MD5 code, we have just changed the content of the HTML, that is, the parameters changed, MD5 naturally changed, MD5 a change will have to regenerate HTML fragments.
So the first parameter of the cache method is the key, and its content is the basis for judging the re-generation of HTML fragments.
After changing the HTML fragment code, the HTML fragment is regenerated, but what if we add a record to the articles? By trying to discover that the HTML fragment is not regenerated.
I'll pass @artilces as the first parameter to the cache method.
.Row-Cache@articlesDo.Col-Md-6.Panel.Panel-Default.Panel-HeadingDiv recent article .-body - @articles .do | Article| p. Clearfix span. Pull-right = datetime article< Span class= "P". created_at = link_to article.< Span class= "NF" >titlearticle_path (article)
Discovery generated Write fragment
, which indicates that it is possible for the page to take effect.
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 "=" ORDER by Visit_count DESC LIMIT [["Published", "T"]]
However, in addition, there are SQL statements generated, and every subsequent request has, even if the cache is hit, because @articles as the first parameter, its content is to be found through the database.
There is a solution to this: put @articles content in the cache, so that you do not have to find the database every time, and once the update or create data, let @articles expire or regenerate.
To facilitate testing, we first changed the storage of the cache to Redis to store the data.
Add the following two lines to the Gemfile file to execute bundle
.
gem ‘redis-namespace‘gem ‘redis-rails‘
config/application.rb
Add the following line in the.
config.cache_store = :redis_store, {host: ‘127.0.0.1‘, port: 6379, namespace: "rails365"}
@articles
The content to be obtained from Redis, mainly read the value of the health in 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 endend
Create or generate a article record to invalidate the data for Redis.
class admin:: Articlescontroller < admin::BaseController Span class= "P". def create @article = article< Span class= "P". new (article_params) rails< Span class= "P". cache. "articles" .endend
This can be refreshed more than two times, you will find no longer check the database, unless you add or modify the article (article).
End
Implementation of the cache system by Redis (vi)