Кеширование страниц средствами Ruby on Rails

25 февраля 2010, обновлена 06 марта 2011

Я тут занялся оптимизированием RentFeed, где большую часть страниц можно положить в кеш и обновлять несколько раз в день. Самый тупой и быстрый кеш в виде статических файлов, отдаваемых непосредственно апачем.

Более сложный случай кеширования рассматривается в статье Кеширование страниц с динамическими элементами средствами Ruby on Rails.

Рельсы умеют такое делать «из коробки», практически одной строчкой:

class SomeController < ApplicationController
  caches_page :index

  def index
  end
end

Все! Кеш уже работает. Правда, почему-то рельсы не предусматривают две вещи. Кеш не работает с GET-параметрами – раз. Хранится прямо в public – два.

GET-параметры

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

Для меня это в первую очередь касалось постраничной разбивки. Если ты используешь will_paginate, то тебе достаточно вынести URL с номером страницы в таблицу роутов:

Rails 2.3

# config/routes.rb
map.resources :items
map.paged_items '/items/page/:page', :controller => 'items', :action => 'index'

Rails 3

# config/routes.rb
resources :items
get '/items/page/:page', :to => 'items#index', :as => :paged_items

Перемещаем кеш в более удобное место (например, в public/cache)

Путь к кешу указывается в конфиге:

# config/environments/production.rb
config.action_controller.page_cache_directory = File.join(Rails.root, 'public', 'cache')

Осталось научить Apache забирать кеш из нужного каталога. Решается это довольно простыми реврайтами, не считая того, что отдавать нужно только файлы, которые точно лежат в кеше – иначе Rails станет получать неправильные пути и перестанет работать.

В общем, нужен вот такой .htaccess:

# public/.htaccess
RewriteEngine On

# убираем слеш в конце пути
RewriteCond %{REQUEST_URI} ^([^.]+)/$
RewriteRule ^[^.]+/$ /%1 [QSA,L]

# перенаправляем в кеш, только если там есть файл
RewriteCond %{THE_REQUEST} ^(GET|HEAD)
RewriteCond %{DOCUMENT_ROOT}/cache/%1 -f
RewriteRule ^.+$ /cache/%1 [QSA,L]

# то же для страниц без расширения (к ним Rails приписывает .html)
RewriteCond %{THE_REQUEST} ^(GET|HEAD)
RewriteCond %{REQUEST_URI} ^([^.]+)$
RewriteCond %{DOCUMENT_ROOT}/cache/%1.html -f
RewriteRule ^[^.]+$ /cache/%1.html [QSA,L]

# то же для пустого пути
RewriteCond %{THE_REQUEST} ^(GET|HEAD)
RewriteCond %{DOCUMENT_ROOT}/cache/index.html -f
RewriteRule ^$ /cache/index.html [QSA,L]

или же такой конфиг для nginx:

if ($request_method !~ ^(GET|HEAD)$) {
  break;
}

location ^~ /cache {
  internal;
}

if (-f $document_root/cache/index.html) {
  rewrite ^/$ /cache/index.html last;
}

if (-f $document_root/cache/$request_uri) {
  rewrite .* /cache/$request_uri last;
}

if (-f $document_root/cache/$request_uri.html) {
  rewrite .* /cache/$request_uri.html last;
}

Очистить весь кеш можно обыкновенным rm -rf public/cache/*.



Пять комментариев. Напиши еще один
  1. F3360fb509c2872419a2f05ad4ce98c2 # 19 марта 2010 Макс написал:

    Спасибо за материал. Как раз то, что искал.

  2. 487812e3de51095e77ed6c8685c27ef6 # 14 июля 2010 Серёга написал:

    # убираем слеш в конце пути
    RewriteCond %{REQUEST_URI} ^([.]+)/$
    RewriteRule ^[.]+/$ /%1 [QSA,L]

    У меня на сайте не работает((( я уже все перепробовал… в чем может быть дело????

  3. Ab074ca0a48f06ab110c374fc7db258e # 06 марта 2011 Kir (iempire.ru) написал:

    А как в Rails 3? Он у меня на map.resources ругается… :(

    1. 777894ea5153122bfa6b83f5bbf23622 # 06 марта 2011 Леонид Шевцов (автор) написал:

      Обновил статью до Rails3 и nginx. :) Что касается роутов, то

      resources :items
      get '/items/page/:page', :to => 'items#index', :as => :paged_items
      
      1. Ab074ca0a48f06ab110c374fc7db258e # 07 марта 2011 Kir (iempire.ru) написал:

        Спасибо!
        Просьба такая, не могли бы вы в одном из следующий постов описать поднятие продакшн-сервера для Rails через nginx?
        Второй день с passenger'ом воюю, не выходит ничего.

(нужна разметка?)

  • **жирный**
  • > цитата

отменить