Использование Memcached с Ruby on Rails на Ubuntu 12.04 LTS

Memcached – это быстрая система  кэширования объектов in-memory (т.е. кэшированные объекты ведут себя как дисковые); данная система может значительно ускорить Rails. Это руководство описывает изменения, которые необходимо внести, чтобы этого достичь.

Требования

Для выполнения руководства нужно предварительно установить Ruby on Rails и Memcached; подробные инструкции можно найти в следующих статьях:

Кроме того, понадобится развёрнутое приложение Rails, готовое к оптимизации при помощи Memcached.

Установка gem-а Dalli

Для начала нужно установить Dalli Gem

gem install dalli

При использовании Bundler нужно добавить строку gem ‘dalli’ в Gemfile и запустить:

bundle install

Это самый быстрый и функциональный способ получить доступ к Memcached.

Настройка Rails для поддержки memcached

Сначала нужно отредактировать файл config/environments/production.rb и внести в него следующую строку, благодаря которой Rails сможет использовать Dalli:

config.cache_store = :dalli_store

Затем нужно настроить ActionController для выполнения кэширования. Для этого внесите в данный файл строку:

config.action_controller.perform_caching = true

После этого перезапустите приложение Rails.

Настройка приложения Rails

Чтобы внесённые изменения вступили в исполнение, нужно обновить приложение Rails. Достичь ускорения сайта с помощью Memcached можно двумя способами.

Добавление заголовков Cache-Control

Самый простой способ применения memcached – это заголовки Cache-Control. Это позволит Rack::Cache хранить результаты экшена в memcached. К примеру, если следующий экшен находится в app/controllers/slow_controller.rb:

def slow_action
sleep 15
# todo - print something here
end

Можно добавить следующую строку, благодаря которой Rack::Cache будет хранить результаты 5 минут:

def slow_action
expires_in 5.minutes
sleep 15
# todo - print something here
end

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

Обратите внимание: при этом заголовки Cache-Control будут общедоступными. Если некоторые экшены должны быть доступны только одному пользователю, используйте:

expires_in 5.minutes, :public => false

Также нужно самостоятельно определить оптимальное время для кэширования: для каждого приложения оно индивидуально.

Хранение объектов в memcached

Если приложение выполняет ресурсоёмкую операцию или постоянно должно создавать какой-то объект, попробуйте хранить их в Memcached. Допустим, экшн выглядит так:

def slow_action
slow_object = create_slow_object
end

Чтобы сохранить результат в memcached, можно изменить экшн:

def slow_action
slow_object = Rails.cache.fetch(:slow_object) do
create_slow_object
end
end

Теперь Rails будет запрашивать в memcached объект с ключом slow_object; если таковой объект не найден, Rails выполнит заданный код и внесёт объект в slow_object.

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

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

Чтобы кэшировать самые динамические части видов, внесите их в блок cache:

<% # app/views/managers/index.html.erb  %>
<% cache manager do %>
Manager's Direct Reports:
<%= render manager.employees %>
<% end %>
<% # app/views/employees/_employee.html.erb %>
<% cache employee do %>
Employee Name: <%= employee.name %>
<%= render employee.incomplete_tasks %>
<% end %>
<% # app/views/tasks/_incomplete_tasks.html.erb %>
<% cache task do %>
Task: <%= task.title %>
Due Date: <%= task.due_date %>
<% end %>

Данная техника кэширования называется Матрёшки.  Rails будет кэшировать эти фрагменты в memcached и, поскольку имя модели было добавлено в качестве параметра cache, ключи этих объектов кэша будут меняться вместе с объектом. Однако при обновлении модели todo могут возникнуть проблемы:

@todo.completed!
@todo.save!

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

class Employee < ActiveRecord::Base
belongs_to :manager, touch: true
end
class Todo < ActiveRecord::Base
belongs_to :employee, touch: true
end

При обновлении модели Todo кэш фрагментов станет недействителен, а модель Employee получит извещение о том, что она также должна обновить свои фрагменты. Затем фрагмент модели  Employee известит модель Manager, после чего обновление кэша будет завершено.

Есть ещё одна проблема при кэшировании по типу «Матрёшек». При развёртывании нового приложения Rails не знает, когда проверять шаблон вида на наличие изменений. Если обновить партиал вида задачи:

<% # app/views/tasks/_incomplete_tasks.html.erb %>
<% cache task do %>
Task: <%= task.title %>
Due Date: <%= task.due_date %>
<p><%= task.notes %></p>
<% end %>

Rails не будет сбрасывать фрагменты кэша, использующие партиал вида. Ранее нужно было добавлять номера версий в cache, но теперь для этого используется gem по имени cache_digests, который автоматически добавляет хеш MD5 файла шаблона в ключ кэша. Если обновить и перезапустить приложение, ключ кэша более не будет подходить, поскольку MD5 файла вида изменился, и Rails снова будет выводить этот шаблон. Этот gem также обрабатывает зависимости между шаблонами файлов; таким образом, в приведенном выше примере все объекты кэша по цепи зависимостей будут недействительными при обновлении the_incomplete_tasks.html.erb.

Эта функция включена в Rails 4.0 по умолчанию. Чтобы использовать этот gem в Rails 3, введите следующую команду:

gem install cache_digests

При использовании Bundler добавьте в Gemfile:

gem 'cache_digests'

Продвинутая настройка Rails и memcached

Dalli Gem очень мощный и может распространять ключи кэша между серверами кластера, на которых установлена система memcached; это позволяет распределить нагрузку и увеличить потенциал memcached. Для этого нужно установить Memcached на каждом из серверов кластера добавить их в config/environments/production.rb:

config.cache_store = :dalli_store, 'web1.example.com', 'web2.example.com', 'web3.example.com'

Это позволит использовать последовательное хэширование для распространения ключей между доступными серверами.

Tags: , , , ,

Добавить комментарий