Статьи

Основы кеширования и кеш-дайджестов

Это введение в кеширование в Rails. Возможно, вы знаете, что Rails выполняет некоторое кэширование «автоматически», но вы не совсем уверены, что это значит или что это такое. Читай дальше что бы узнать.

Короче говоря, кэширование дает нам быстрый доступ к значениям, не затрагивая более дорогой магазин, такой как база данных. Таким образом, мы можем улучшить производительность нашего приложения Rails. Rails будет кэшировать практически все, от объекта до страницы и до частичного.

Rails в основном предоставляют три метода кэширования.

  • Кэширование страниц
  • Кеширование действий
  • Кэширование фрагментов

Прежде чем мы начнем, убедитесь, что вы включили кэширование в своем файле среды.

config.action_controller.perform_caching = true

Это верно по умолчанию для production.rbdevelopment.rbtest.rb

Кэширование страниц

Кэширование страниц — это механизм, который сохраняет выходные данные действия в файле. поэтому при поступлении запроса на веб-сервер можно ответить, не проходя через Actionpack. Чтобы попробовать это, я создал образец приложения. Я создал блог с пометками title и description. В нашем контроллере блогов у нас есть действие index, и мы хотим его кэшировать. Для этого вызовите метод caches_page для действия, которое мы хотим кэшировать.

 class BlogsController < ApplicationController
  caches_page :index
  def index
    @blogs = Blog.all
  end
end

Rails создает файл в / public каталоге с именем blogs.html. Когда мы запускаем наш сервер и делаем запрос на ‘/ blogs’, Rails кэширует первый вист на страницу в вышеупомянутом файле blogs.html. В любое время после этого вы увидите этот кэшированный файл, как показано в следующей записи журнала:

 Write page /Users/rashmiyadav/crap/cache_digest_test/public/blogs.html (0.7ms)

Если вы хотите кэшировать другое действие, просто добавьте его в метод caches_page

 caches_page :index, :show

Чтобы истечь срок действия страницы, чтобы мы могли кэшировать новую версию, выполните вызов expire_page:

 class BlogsController < ApplicationController
  caches_page :index
  def  index
    @blogs = Blog.all
  end
  def create
    expire_page :action => :index
    @blog = Blog.new(params[:blog])
    @blog.save
  end
  def update
    expire_page :action => :index
    @blog = Blog.find(params[:id])
    @blog.update_attributes(params[:blog])
  end
end

Каждый раз, когда мы обновляем или создаем запись, срок ее действия истекает. Вы можете проверить это, снова используя журналы:

 Expire page /Users/rashmiyadav/crap/cache_digest_test/public/cache/blogs.html (0.2ms)  

Метод expire_page принимает несколько аргументов, таких как путь кешированной страницы, такой как «/ blogs», или хэш с действием, именем контроллера и форматом, срок действия которого истекает.

 expire_page :controller => 'blogs', :action  => 'show', :format => 'json'

конфигурация

По умолчанию Rails хранит файлы кэша в каталоге / public, но мы можем это изменить. Просто измените значение page_cache_directory в файле config / application.rb:

 config.action_controller.page_cache_directory = Rails.public_path + "/cache" 

Теперь все файлы кеша будут в каталоге / public / cache. Следует помнить одну вещь: если вы работаете с сервером локально в процессе разработки и у вас есть старые файлы кэша в вашем общем каталоге, сервер продолжит собирать старые файлы кэша в общем каталоге. Другими словами, не забудьте удалить старые файлы кэша из публичного каталога.

Другим элементом конфигурации для кэширования страниц является расширение файла кэша. Расширение файла кэша зависит от входящего запроса. Если это json, то файл кэша будет «.json», а если запрос будет xml, то файл кэша будет «.xml» и так далее. Если запрос не имеет какого-либо определенного расширения, то по умолчанию он получает расширение «.html». Мы можем настроить это используя config.action_controller.page_cache_extension .

Кэширование страниц — это один из способов ускорить работу вашего приложения, но это не вариант, если у вас есть контент, который стоит за аутентификацией. Давайте посмотрим на другие методы кэширования в Rails.

Кеширование действий

Кэширование действий аналогично кэшированию страниц, за исключением того, что фильтры выполняются до того, как происходит кэширование. Давайте возьмем пример нашего блога:

 class BlogsController < ApplicationController
  before_filter :authenticate
  caches_action :index
  def index
    @blogs = Blog.all
  end
end

мы вызываем caches_action для кеширования действия индекса. Когда мы впервые нажимаем на ссылку http: // localhost: 3000 / blogs, появляется следующая запись в блоге:

 Write fragment views/localhost:3000/blogs (1.8ms)

Теперь каждый раз, когда мы вызываем действие index, он запускает метод authenticate перед тем, как дать кэшированный ответ. В этом главное отличие действий от кэширования страниц. Кэширование действий запускает все фильтры перед обслуживанием кеша.

Срок действия кэша

Чтобы истечь срок действия кэша, нам просто нужно вызвать expire_action:

 class BlogsController < ApplicationController
  caches_action :index
  def index
    @blogs = Blog.all
  end
  def create
    expire_action :action => :index
    @blog = Blog.new(params[:blog])
    @blog.save
  end
  def update
    expire_action :action => :index
    @blog = Blog.find(params[:id])
    @blog.update_attributes(params[:blog])
  end
end

Когда мы обновляем и создаем запись, срок ее действия истекает. Вот соответствующая запись в журнале:

 Expire fragment views/localhost:3000/blogs (59.0ms)

Метод expire_action принимает те же аргументы, что и expire_page выше:

 expire_action(:controller => :blogs, :action => :show, :id => 25)

конфигурация

Если нам нужно кэшировать действие, основанное на некоторых условиях, мы можем сделать это с помощью: if или: кроме как с proc. Например, если мы не хотим кэшировать действие index для запросов json:

 class BlogsController > ApplicationController
  caches_action :index, :if => Proc.new{|c|!c.request.format.json?}
  def index
    @blogs = Blog.all
    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @blogs }
      format.csv
    end
  end
end

Мы также можем настроить путь кеша по умолчанию.

 caches_action :show, :cache_path => { :project => 1 }

Если вы передадите: layout => false, он будет только кэшировать ваш контент действий. Это полезно, когда ваш макет содержит динамическую информацию.

Кэширование фрагментов

Кэширование страниц и кэширование действий одновременно кэшируют всю страницу или все действие, но что, если нам просто нужно кэшировать часть представления? Это где кеширование фрагментов вступает в игру. У нас обычно есть представление с динамическим содержимым, поэтому мы не можем сохранить всю страницу. Тем не менее, вполне вероятно, что мы можем кэшировать биты страницы и удалять их по мере необходимости.

Если мы показываем сообщение в блоге с их комментариями, мы можем кэшировать комментарии самостоятельно:

 <div>
  <%= @blog.title %><%= @blog.desc %>
</div>

<%cache("blog_comments_#{@blog.id}") do%>
  <h3>Comments</h3>
  <% @blog.comments.each_with_index do|c, i|%>
    <div><%= c.value%><span><%= c.created_at.strftime('%B %d %Y')%></span></div>
  <%end%>
<%end%>
<%= link_to 'Add New comment', new_blog_comment_path(@blog)%>

Раздел комментариев должен быть изменен при добавлении нового комментария, и мы кешируем его с помощью ключа «blog_comments_#[email protected]}»). Вот что говорит журнал:

 Write fragment views/blog_comments_1

Expire Cache

Чтобы истечь срок действия этого кэша, вызовите метод expire_fragment . Здесь он вызывается при создании нового комментария:

 class BlogsController < ApplicationController
  def create
    @blog = Blog.find params[:blog_id]
    expire_fragment("blog_comments_#{@blog.id}")
    @comment = @blog.comments.new(params[:comment])
  end
end

конфигурация

Если имя ключа не предоставлено для функции кэширования, то Rails будет кэшировать блок на основе имени действия контроллера. В этом случае вы можете завершить кеш, вызвав expire_fragment (: controller => ‘blogs’,: action => ‘show’).

Это, очевидно, приведет к возникновению конфликтов, если

  • Мы также кешируем действия
  • И наличие нескольких фрагментов кеша за действие

Мы можем добавить суффикс действия, чтобы избежать таких конфликтов:

 cache(:action => 'show', :controller => 'blogs', :action_suffix => 'blog_comments' )

Чтобы истечь этот кеш:

 expire_fragment(:controller => 'blogs', :action => 'show',:action_suffix => 'blog_comments')

Подметальные

Вызов метода expire внутри действия контроллера — плохая идея. Rails предоставляет другой механизм для этого процесса истечения: Sweepers . Подметальные машины несут ответственность за истечение срока действия кэшей. Они наблюдают за моделями и выполняют обратные вызовы, как определено в уборщике.

Например, я создал файл blog_sweeper.rb:

 class BlogSweeper < ActionController::Caching::Sweeper
  observe Blog
  def after_update(blog)
    expire_action(:controller => :blogs, :action => :index)
  end
  def after_create(blog)
    expire_action(:controller => :blogs, :action => :index)
  end
end

Но нам нужно вызвать этот контроллер в нашем контроллере для вызова метода cache_sweeper в контроллере

 class BlogController < ApplicationController
  cache_sweeper :blog_sweeper, :only => [:index]
end

Кэш Дайджесты

Кэш-дайджесты — лучший способ справиться с кэшированием фрагментов. Он основан на схеме « Русская кукла» , то есть, когда у вас есть вложенные кэшированные фрагменты и изменения вложенного содержимого, срок действия кэша только для этого содержимого истекает, и остальная часть кэша используется повторно. Вот пример из нашего блога. Наша модель структуры выглядит так:

 class User < ActiveRecord::Base
  has_many :blogs
end
# Blog model
class Blog < ActiveRecord::Base
 has_many :comments
 belongs_to :user, :touch => true
end
#comment model
class Comment < ActiveRecord::Base
 belongs_to :blog, :touch => true
end

Мы используем сенсорную опцию, поэтому, если мы обновим комментарий, блог получит новое значение updated_at.

Вот «users / show.html.erb», показывающий подробности пользователя с их блогом и комментариями:

 <%cache @user do%>
  <div>  Name:<%= @user.name %></div>
  <div>  Email:<%= @user.email %></div>
  <%= render @user.blogs%>
<%end%>
<!-- 'blogs/blog.html.erb' -->
<%cache blog do%>
  <div><%= blog.title %></div>
  <div><%= blog.desc %></div>
  <%= render blog.comments%>
<%end%>
<!-- 'comments/comment.html.erb' -->
<%cache comment do%>
  <%= comment.value %>
<%end%>

Вы можете видеть, что мы кешируем комментарии внутри блога и блог внутри пользователя. В примере, где у нас есть один пользователь с 2 сообщениями в блоге и 1 комментарием, ключи кэша выглядят следующим образом:

 views/users/1-20130118063610
views/blogs/1-20130118063600
views/comments/1-20130118063600
views/blogs/2-20130118063610

Это сочетание пути просмотра, идентификатора объекта и отметки времени последнего обновления. Эта комбинация гарантирует, что срок действия кэша всегда истекает при обновлении модели. Если мы добавим новый блог или отредактируем блог, у него истечет срок действия кеша пользователя вместе с кешем блога. потому что мы использовали сенсорный вариант в нашей модели блога.

Аналогично, если мы добавим или отредактируем комментарий, все истекает. Однако если мы изменим что-либо в самом представлении, например новый стиль, перезагрузка страницы не будет отражать это изменение, так как срок действия ключей кэша не истек. Мы можем смягчить это, сохранив версию ключей кэша и изменив эту версию по мере необходимости.

 <%cache ['v1', @user] do%>
  <div>Name:<%= @user.name %></div>
  <div>Email:<%= @user.email %></div>
  <%= render @user.blogs%>
<%end%>
<!-- 'blogs/blog.html.erb' -->
<%cache ['v2',blog] do%>
  <div><%= blog.title%></div>
  <div><%= blog.desc %></div>
  <%= render blog.comments%>
<%end%>
<!-- 'comments/comment.html.erb' -->
<%cache ['v3',comment] do%>
  <%= comment.value %>
<%end%>

Каждый раз, когда мы изменяем наше представление, нам нужно обновить версию ключа кеша. Теперь у нас больше проблем:

  • Нам нужно запомнить номер версии шаблона
  • Версии родительского шаблона должны быть изменены при изменении дочернего шаблона.
  • Если один и тот же фрагмент используется в разных представлениях, то трудно поддерживать версии

Не паникуйте! Rails всегда старается сделать вещи проще. В Rails 4 были добавлены cache_digests для поддержки кэширования вложенных фрагментов.

При использовании Cache Digests нам не нужно беспокоиться об истечении срока действия кэширования и управления версиями, когда у нас есть вложенное кэширование фрагментов. Кстати, вы можете использовать дайджесты кеша в Rails 3.X, добавив гем cache_digests в ваш Gemfile.

 <%cache @user do%>
  Name:<%= @user.name %>
  Email:<%= @user.email %>
  <%= render @user.blogs%>
<%end%>
<!-- 'blogs/blog.html.erb' -->
<%cache blog do%>
  <%= blog.title %>
  <%= blog.desc %>
  <%= render blog.comments%>
<%end%>
<!-- 'comments/comment.html.erb' -->
<%cache comment do%>
  <%= comment.value %>
<%end%>

При рендеринге пользовательского шоу будет сгенерирован лог:
cache_digest1

К ключу кэша для пользователей / шоу добавляется MD5 самого шаблона и всех его зависимостей. Если мы изменим что-либо в любом представлении (users / show, _blog или _comment) и перезапустим сервер, он автоматически истечет срок действия ключа кеша и пересчитает новый ключ кеша.

В производстве нам не нужно беспокоиться, потому что каждый раз, когда мы разворачиваем наше приложение, оно перезагружает сервер.

Проверьте зависимости шаблона

Чтобы проверить зависимости представления, гем cache_digests предоставляет две задачи rake

1. rake cache_digests: зависимости TEMPLATE = users / show

Я получил следующий вывод, запустив вышеуказанную задачу

dependency_task

Это дает вам зависимости только от пользователя / представления представления.

2. rake cache_digests: nested_dependencies TEMPLATE = users / show, который дает вывод

nested_dependencies_task

Он дает вам зависимости представления пользователя / шоу вместе со всеми его вложенными представлениями.

Другой способ вызвать зависимости:

1. В рендере мы можем вызвать метод, явно указав частичное имя и коллекцию. Например:

 <%= render @user.last_one_month_blogs%>

Таким образом, мы должны упомянуть частичное имя и коллекцию здесь

 <%= render :partial => 'blogs/_blogs', :collection => @user.last_one_month_blogs%>

2. Если мы используем помощника в нашем вызове рендеринга. Затем нам нужно использовать комментарий, который укажет зависимость шаблона

 <%# Template Dependency: comments/comment %>
<%= render_comments(blog)%>

Заворачивать

По сути, я хотел объяснить кеширование в Rails и сколько способов кеширования можно выполнить.

Больше ресурсов -: