Cache'owanie w Rails - podstawy

Wysłane przez Marek Tenus (~marcus) dnia 05.10.2007

Cache'owanie to jedno z rozwiązań, dzięki któremu możemy znacznie przyśpieszyć działanie naszego projektu. Rails mają kilka rozwiązań, które umożliwiają nam poprawienie wydajności naszej aplikacji dzięki wykorzystaniu mechanizmu cache'owania.

Cache'owanie to jedno z rozwiązań, dzięki któremu możemy znacznie przyśpieszyć działanie naszego projektu. Rails mają kilka rozwiązań, które umożliwiają nam poprawienie wydajności naszej aplikacji dzięki wykorzystaniu mechanizmu cache'owania. Poniższy tutorial jest przede wszystkim oparty na opracowaniu na ten temat Bruce Tate'a (autora kilku książek o Java, Ruby i Ruby on Rails). Źródło tego opracowania możecie znaleźć tutaj.

Przed rozpoczęciem cache'owania należy w /config/environments/development.rb ustawić:

config.action_controller.perform_caching = true

Cache'owanie stron statycznych

Pod określeniem stron statycznych rozumiemy te strony aplikacji internetowej, których treść nie jest  zmieniania (bądź zmieniana naprawdę rzadko). Mechanizm cache'owania  stron statycznych w Rails jest bardo prosty a jego wynikiem jest stworzenie statycznej strony w katalogu public. By włączyć cache'owanie dla danej strony i wygenerować ją w postaci statycznej wystarczy w kontrolerze zadeklarować to w natępujący sposób:

 

class UserController < ApplicationController
  caches_page :regulamin, :o_nas 
end

gdzie symbol jest odpowiednikiem nazwy akcji kontrolera, w którym obsługiwana jest dana strona.

By wykonać przeładowanie zawartości strony statycznej (usunąć i wygenerować nowe), należy zadeklarować sobie akcję, która taką czynność będzie wykonawała i wykorystać metodę expire_page.

 class UserController < ApplicationController   
  caches_page :regulamin, :o_nas      
  def reload_page     
    expire_page :action => :regulamin
    expire_page :action => :o_nas   
  end  
end  

 Pamiętaj, aby URL cache'owanej strony nie zawierał parametrów ei. ?param1=value, lecz mapuj URL'e do postaci param1/value wykorzystując Routes (routes.rb).

 
map.connect 'profil/:id/:name', :controller => 'user', :action => 'show' 

Pamiętaj jednak, by cache'ować jedynie, te strony, do których dostęp będą mieli wszyscy użytkownicy (regulamin, faq etc.).

Jeśli zależy ci by cache'owane strony były co określony czas expirowane lub chcesz mieć możliwość sterowania cachowe'owaniem możesz zaimplementować sobie Sweeper'a.

 
class UserController < ApplicationController    
   cache_sweeper :user_sweeper 
   ...  

class UserSweeper < ActionController::Caching::Sweeper

  observe User

  def after_create(data)
    expire_user(data)
  end

  def after_save(data)
    expire_user(data)
  end

  def after_destroy(data)
    expire_user(data)
  end

  def expire_user(data)
    FileUtils.rm_rf File.expand_path("public/user/#{data.id}", RAILS_ROOT)
    FileUtils.rm_rf File.expand_path("public/index.html", RAILS_ROOT)
  end
end 

Cache'owanie akcji

Możemy również cache'ować akcje podobnie jak robiliśmy to ze stronami. Mechanizm działania tego cache'owania jest jednak nieco niż poprzedni. Rails wywoła kontroler zanim wyrenderuje akcję. Jeśli strona renderowana w danej akcji jest już  zacache'owana, wówczas Rails wyrenderuje tą stronę już zacache'owaną zamiast renderować ją od nowa. Pomimo, że cache'owanie w ten sposób jest wolniejsze od cache'owania stron statycznych to jednak pozwala na cachowanie stron, do których dostęp powinni mieć jedynie określeni użytkownicy (np. po autoryzacji).

 
class UerController < ApplicationController
  caches_action :schowek  
end

Cache'owanie fragmentów strony

Dość ciekawym mechanizem jest cache'owanie fragmentaryczne. Załóżmy, że na stronie znajdują się elementy statyczne, a całościowo dana strona jest dynamiczna. Właśnie wówczas do statycznych fragmentów strony możemy użyć tego rodzaju cachowania. W kodzie html należy określić obszar, który będzie objęty częściowym cache'owaniem.

<% cache 'left_box' do %>
<div id="left_box">
    <h2><%= @left_box_title %></h2>
    <div>
        <%= @left_box_content %>
    </div>
</div>
<% end %>

Możemy również wskazać lokacje dla cache'owanych elementów (domyślnie są one zapisywane do katalogu public/) umieszczając w environment.rb naprzykład:

ActionController::Base.fragment_cache_store = :file_store, "/public/boxy"

Memcached

W Rails oprócz cache'owania powyżej przedstawionego możemy również w tym celu wykorzystać serwis Memcache. Cache'owanie z wykorzystaniem tego mechanizmu jest o wiele szybszy i ma większe możliwości umożliwiając również częściowe i całkowite cache'owanie modeli (oczywiście należy do tego cache'owania podejść ostrożnie i nie przesadzić). Objekty są serializowane za pomocą algorytmu Marshala (czyli naprawdę szybko). Przede wszystkim najpierw zainstalujmy potrzebny nam plugin (memcache-client jest składową tego gem'a ):

sudo gem install cached_model

oraz skonfigurujmy nasz serwis w pliku environment.rb (dla wersji produkcyjnej w production.rb):
memcache_options = {
  :c_threshold => 10_000,
  :compression => false,
  :debug => false,
  :readonly => false,
  :urlencode => false,
  :ttl => 300,
  :namespace => 'test',
  :disabled => false
}

CACHE = MemCache.new memcache_options
CACHE.servers = 'localhost:11211'

a następnie uruchomić serwis jako daemona (z parametrem -vv uruchomimy serwer z domyślną pamięcią 64 MB)

memcached -d

A także sami możemy naprzykład podać ustawienia dla serwera przy jego uruchamianiu z pamięcią 2GB (-m 2048), jako daemona (-d) na porcie (-p) 11211 przy nasłuchu dla IP (-l) 10.0.0.40:

memcached -d -m 2048 -l 10.0.0.40 -p 11211

Pozostaje już nam jedynie korzystać z możliwości serwera i używać zacache'owanych modeli:

class User < CachedModel

end

Źródła: