Memcache w Django: krok ku lepszej skalowalności
Podczas tworzenia aplikacji webowych, warto w miarę wcześnie pomyśleć o skali z jaką nasze rozwiązanie będzie miało się w przyszłości zmierzyć. Niezależnie od tego czy owa aplikacja ma pracować w jednym przedsiębiorstwie, czy próbuje zainstnieć szerzej, w sieci – w każdym przypadku może się okazać, że odniesie sukces. To z kolei sprawi, że ilość jej użytkowników urośnie… Firma może się rozwinąć, przejąć konkurencję, aplikacja webowa może się okazać takim strzałem w dziesiątkę jak nie tak dawno temu Nasza-Klasa – nigdy nie wiadomo
W momencie gdy miałaby go odnieść, nie będzie już zbyt wiele czasu na przebudowę kodu, a napewno nie będzie go na jego całkowitą reorganizację.
Jeśli rozwiązanie jest skryptem uruchamianym od święta – może to być zwykły skrypt CGI. Jeśli jednak miałby być uruchamiany częściej – wartałoby już pomyśleć o mod_php czy mod_python… Albo jeszcze lepiej – fastcgi, a w przypadku pythona – wsgi. Kiedy użycie aplikacji rośnie nadal – frontend mnożymy na kolejne węzły stawiając przed nimi balancera robiącego za reverse proxy. A w backendzie klastrujemy… ale co na styku macierz, klaster? Ilość zapytań do bazy rośnie, io waity zaczynają rosnąć i robi się nieciekawie… Da się to rozwiązać?
Jak to robi Google
Google od jakiegoś czasu pokazuje nam jak powinny być tworzone intensywnie wykorzystywane aplikacje webowe i na jak zorganizowanym backendzie powinny pracować, aby skalowały się najefektywniej. Myślę, że warto z tej wiedzy skorzystać zanim io waity zaczną spędzać nam sen z powiek.
Miałem okazję napisać sobie kilka małych aplikacji które pracują w chmurze na platformie Google – tzw. Google App Engine (w skrócie GAE). Warto było poświęcić trochę czasu aby to rozwiązanie poznać. Łatwiej było mi dzięki temu można zrozumieć między innymi dlaczego poszczególne serwery Google nie są potężnymi serwerami rackowymi z bardzo silnymi procesorami, ale raczej „lżejszymi” serwerkami, ale za to wypełnionymi po brzegi koścmi pamięci.
Dlaczego takie lżejsze maszyny? Poza oszczędnością energii i pieniędzy są one wystarczające do obsługi pythonowych frameworków, w przeciwieństwie do tych PHP’owych.
Google skupia się na Javie i Pythonie, gdzie prawie pewne jest to, że ich rozproszony Datastore – system bazodanowy, został stworzony w oparciu o język Python. Język ten znany jest m.in z wydajności, szczególnie, jeśli osoba która go używa stosuje się do najbardziej podstawowych zasad jak nie allokowanie ogromnej ilości pamięci itp. Dlaczego więc w serwerach Google tyle pamięci? Odpowiedzią jest memcache.
Google chwali się tym, że stworzyli sobie własną implementację cache, inspirowaną oprogramowaniem memcached i zachęca, aby developerzy aplikacji App Engine’owych również korzystali z memcache’a.
Na czym polega memcache
Uproszczony graf przedstawiający działanie memcache’u:
Koncepcja działania jest prosta – zamiast odwoływać się bezpośrednio do bazy danych, odwołujemy się do memcache’a – jeśli trafiliśmy na dane (hit), wykorzystujemy je, jeśli nie (miss), dopiero wtedy odwołujemy się do bazy, jednocześnie zapisując pobrane z bazy dane w naszym cache’u. Dzięki temu prostemu zabiegowi, nasza strona, która podczas każdego odwołania do niej potrzebuje jakichś danych z bazy, jest w stanie funkcjonować praktycznie w ogóle jej o nic nie odpytując – wszystko co potrzebne będzie trzymać w pamięci podręcznej.
Zastosowanie i dokumentacja
Aby wykorzystać memcache w Google App Engine, polecam zapoznanie się z dokumentacją na stronach Google.
A co jeśli chcielibyśmy wykorzystać memcache w Django? Naturalnie – nic nie stoi na przeszkodzie – Django posiada coś takiego jak cache framework, który na dodatek jest bardzo dobrze udokumentowany.
Django samo z siebie jest przystosowane do wykorzystania cache’u przy obsłudze widoków czy template’ów, ale również oferuje dostęp niskopoziomowy – niemalże analogiczny do tego z Google App Engine… Niemalże, bo jakoś wersja Google bardziej przypadła mi do gustu. Dlatego przygotowałem sobie prosty interfejs, który wydaje mi się być odrobinę bardziej logiczny. Kod oczywiście załączam. Mam nadzieję, że komuś się przyda.
Konfiguracja systemu, projektu Django oraz moja niskopoziomowa obsługa
Zakładając, że pracujemy na systemie GNU Debian/Ubuntu oraz Django jest już w systemie jakąś drogą zainstalowane (osobiście w tym akurat przypadku polecam instalację z oryginalnych źródeł nad paczką Django z dystrybucji), doinstalowujemy obługę memcached, włączamy ją i restartujemy demona memcached:
sudo -i aptitude install memcached python-memcache sed -i 's/no/yes/' /etc/default/memcached service memcached restart exit
teraz w naszym projekcie Django włączamy obsługę cache:
w pliku settings.py:
CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
jeśli chcemy wykorzystać moją małą biblioteczkę poniżej, do settings.py musimy dodać też konfigurację memcache:
class conf:
class memcache:
disabled = False
timeout = 600
Moja niskopoziomowa obsługa memcache.py:
#!/usr/bin/python
# -*- coding: utf-8 -*-
""" cache low-level backend based on memcached """
from django.core.cache import cache
from settings import conf
def get(key, default=None, timeout=conf.memcache.timeout):
""" returns value for given key from cache, and refreshes it
in memcache with automatic conversion from unicode
to str - fix to:
__import__() argument 1 must be string without null bytes, not str """
value = cache.get(key, default)
if value is not default:
cache.set(key, value, timeout)
if type(value) is unicode:
value = str(value)
return value
def set(key, value, timeout=conf.memcache.timeout):
""" sets a value for given key in cache """
cache.set(key, value, timeout)
def delete(key):
""" remove given key from cache """
cache.delete(key)
# in the name of syntatic sugar
disabled = bool(conf.memcache.disabled)
enabled = not disabled
A przykład zastosowania pojawi się jutro albo pojutrze – w nowym wpisie.



Comments
Jedna odpowiedź do wpisu “Memcache w Django: krok ku lepszej skalowalności”Trackbacki
Zobacz co napisali inni...[...] – biblioteczka ta opiera się o opisaną w poprzednim wpisie obsługę memcache’u w Django – co sprawia, że do póki nie zmienimy wartości danych, a ich obecność [...]