Redis
 sql >> Baza danych >  >> NoSQL >> Redis

Buforowanie w Django z Redis

Wydajność aplikacji ma kluczowe znaczenie dla sukcesu Twojego produktu. W środowisku, w którym użytkownicy oczekują, że czas reakcji witryny jest krótszy niż sekunda, konsekwencje powolnej aplikacji można mierzyć w dolarach i centach. Nawet jeśli niczego nie sprzedajesz, szybkie ładowanie stron poprawia komfort odwiedzania Twojej witryny.

Wszystko, co dzieje się na serwerze między momentem otrzymania żądania, a zwróceniem odpowiedzi, wydłuża czas ładowania strony. Ogólna zasada mówi, że im więcej przetwarzania można wyeliminować na serwerze, tym szybciej będzie działać aplikacja. Buforowanie danych po ich przetworzeniu, a następnie udostępnianie ich z pamięci podręcznej przy następnym żądaniu jest jednym ze sposobów na zmniejszenie obciążenia serwera. W tym samouczku omówimy niektóre czynniki, które ugrzęzły w Twojej aplikacji, i zademonstrujemy, jak zaimplementować buforowanie w Redis, aby przeciwdziałać ich skutkom.

Bezpłatny bonus: Kliknij tutaj, aby uzyskać dostęp do bezpłatnego przewodnika po zasobach edukacyjnych Django (PDF), który zawiera wskazówki i triki, a także typowe pułapki, których należy unikać podczas tworzenia aplikacji internetowych Python + Django.


Co to jest Redis?

Redis to magazyn struktury danych w pamięci, który może być używany jako silnik pamięci podręcznej. Ponieważ przechowuje dane w pamięci RAM, Redis może je dostarczyć bardzo szybko. Redis nie jest jedynym produktem, którego możemy użyć do buforowania. Memcached to kolejny popularny system buforowania w pamięci, ale wiele osób zgadza się, że Redis jest lepszy od Memcached w większości przypadków. Osobiście podoba nam się łatwość konfiguracji i używania Redis do innych celów, takich jak kolejka Redis.



Pierwsze kroki

Stworzyliśmy przykładową aplikację, aby wprowadzić Cię w koncepcję buforowania. Nasza aplikacja wykorzystuje:

  • Django (v1.9.8)
  • Pasek narzędzi debugowania Django (v1.4)
  • django-redis (v4.4.3)
  • Redis (v3.2.0)

Zainstaluj aplikację

Zanim sklonujesz repozytorium, zainstaluj virtualenvwrapper, jeśli jeszcze go nie masz. Jest to narzędzie, które pozwala zainstalować określone zależności Pythona, których potrzebuje Twój projekt, co pozwala na osobne kierowanie na wersje i biblioteki wymagane przez Twoją aplikację.

Następnie zmień katalogi, w których przechowujesz projekty i sklonuj przykładowe repozytorium aplikacji. Po zakończeniu zmień katalogi na sklonowane repozytorium, a następnie utwórz nowe środowisko wirtualne dla przykładowej aplikacji za pomocą mkvirtualenv polecenie:

$ mkvirtualenv django-redis
(django-redis)$

UWAGA: Tworzenie wirtualnego środowiska za pomocą mkvirtualenv również go aktywuje.

Zainstaluj wszystkie wymagane zależności Pythona za pomocą pip , a następnie sprawdź następujący tag:

(django-redis)$ git checkout tags/1

Zakończ konfigurowanie przykładowej aplikacji, budując bazę danych i wypełniając ją przykładowymi danymi. Upewnij się również, że utworzyłeś superużytkownika, aby móc zalogować się na stronie administratora. Postępuj zgodnie z poniższymi przykładami kodu, a następnie spróbuj uruchomić aplikację, aby upewnić się, że działa poprawnie. Odwiedź stronę administratora w przeglądarce, aby potwierdzić, że dane zostały poprawnie załadowane.

(django-redis)$ python manage.py makemigrations cookbook
(django-redis)$ python manage.py migrate
(django-redis)$ python manage.py createsuperuser
(django-redis)$ python manage.py loaddata cookbook/fixtures/cookbook.json
(django-redis)$ python manage.py runserver

Po uruchomieniu aplikacji Django przejdź do instalacji Redis.



Zainstaluj Redis

Pobierz i zainstaluj Redis, korzystając z instrukcji zawartych w dokumentacji. Alternatywnie możesz zainstalować Redis za pomocą menedżera pakietów, takiego jak apt-get lub piwo domowe w zależności od systemu operacyjnego.

Uruchom serwer Redis z nowego okna terminala.

$ redis-server

Następnie uruchom interfejs wiersza poleceń (CLI) Redis w innym oknie terminala i sprawdź, czy łączy się z serwerem Redis. Będziemy używać interfejsu Redis CLI do sprawdzania kluczy, które dodajemy do pamięci podręcznej.

$ redis-cli ping
PONG

Redis udostępnia interfejs API z różnymi poleceniami, których programista może używać do działania w magazynie danych. Django używa django-redis do wykonywania poleceń w Redis.

Patrząc na naszą przykładową aplikację w edytorze tekstu, możemy zobaczyć konfigurację Redis w settings.py plik. Definiujemy domyślną pamięć podręczną za pomocą CACHES ustawienie, używając wbudowanego django-redis cache jako nasz backend. Redis domyślnie działa na porcie 6379 i wskazujemy tę lokalizację w naszym ustawieniu. Ostatnią rzeczą, o której należy wspomnieć, jest to, że django-redis dołącza do nazw kluczy prefiks i wersję, aby ułatwić rozróżnienie podobnych kluczy. W tym przypadku zdefiniowaliśmy prefiks jako „przykład”.

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient"
        },
        "KEY_PREFIX": "example"
    }
}

UWAGA :Chociaż skonfigurowaliśmy backend pamięci podręcznej, żadna z funkcji widoku nie ma zaimplementowanej pamięci podręcznej.




Wydajność aplikacji

Jak wspomnieliśmy na początku tego samouczka, wszystko, co serwer robi, aby przetworzyć żądanie, spowalnia czas ładowania aplikacji. Nakład przetwarzania związany z prowadzeniem logiki biznesowej i szablonami renderowania może być znaczny. Opóźnienie sieci wpływa na czas potrzebny na wykonanie zapytania do bazy danych. Czynniki te wchodzą w grę za każdym razem, gdy klient wysyła żądanie HTTP do serwera. Gdy użytkownicy inicjują wiele żądań na sekundę, wpływ na wydajność staje się zauważalny, ponieważ serwer pracuje nad ich przetwarzaniem.

Kiedy wdrażamy buforowanie, pozwalamy serwerowi raz przetworzyć żądanie, a następnie przechowujemy je w naszej pamięci podręcznej. Ponieważ żądania o ten sam adres URL są odbierane przez naszą aplikację, serwer pobiera wyniki z pamięci podręcznej zamiast przetwarzać je za każdym razem od nowa. Zazwyczaj ustalamy czas życia wyników w pamięci podręcznej, aby dane mogły być okresowo odświeżane, co jest ważnym krokiem do wdrożenia w celu uniknięcia udostępniania nieaktualnych danych.

Powinieneś rozważyć buforowanie wyniku żądania, gdy spełnione są następujące przypadki:

  • renderowanie strony wymaga wielu zapytań do bazy danych i/lub logiki biznesowej,
  • strona jest często odwiedzana przez użytkowników,
  • dane są takie same dla każdego użytkownika,
  • a dane nie zmieniają się często.

Zacznij od pomiaru wydajności

Rozpocznij od przetestowania szybkości każdej strony w aplikacji, sprawdzając, jak szybko aplikacja zwraca odpowiedź po otrzymaniu żądania.

Aby to osiągnąć, wysadzamy każdą stronę serią żądań za pomocą loadtest, generatora obciążenia HTTP, a następnie zwracamy szczególną uwagę na częstotliwość żądań. Odwiedź powyższy link, aby zainstalować. Po zainstalowaniu przetestuj wyniki względem /cookbook/ Ścieżka adresu URL:

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

Zauważ, że przetwarzamy około 16 żądań na sekundę:

Requests per second: 16

Kiedy patrzymy na to, co robi kod, możemy podejmować decyzje, jak wprowadzić zmiany, aby poprawić wydajność. Aplikacja wykonuje 3 wywołania sieciowe do bazy danych z każdym żądaniem do /cookbook/ , a każde wywołanie wymaga czasu na otwarcie połączenia i wykonanie zapytania. Odwiedź /cookbook/ URL w przeglądarce i rozwiń kartę Django Debug Toolbar, aby potwierdzić to zachowanie. Znajdź menu oznaczone „SQL” i odczytaj liczbę zapytań:

książka kucharska/services.py

from cookbook.models import Recipe


def get_recipes():
    # Queries 3 tables: cookbook_recipe, cookbook_ingredient,
    # and cookbook_food.
    return list(Recipe.objects.prefetch_related('ingredient_set__food'))

książka kucharska/views.py

from django.shortcuts import render
from cookbook.services import get_recipes


def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

Aplikacja renderuje również szablon z potencjalnie kosztowną logiką.

<html>
<head>
  <title>Recipes</title>
</head>
<body>
{% for recipe in recipes %}
  <h1>{{ recipe.name }}</h1>
    <p>{{ recipe.desc }}</p>
  <h2>Ingredients</h2>
  <ul>
    {% for ingredient in recipe.ingredient_set.all %}
    <li>{{ ingredient.desc }}</li>
    {% endfor %}
  </ul>
  <h2>Instructions</h2>
    <p>{{ recipe.instructions }}</p>
{% endfor %}
</body>
</html>


Zaimplementuj buforowanie

Wyobraź sobie całkowitą liczbę połączeń sieciowych, które wykona nasza aplikacja, gdy użytkownicy zaczną odwiedzać naszą witrynę. Jeśli 1000 użytkowników trafi do interfejsu API, który pobiera przepisy kulinarne, nasza aplikacja prześle zapytanie do bazy danych 3000 razy, a przy każdym żądaniu zostanie wyrenderowany nowy szablon. Liczba ta rośnie wraz ze wzrostem skali naszej aplikacji. Na szczęście ten widok jest świetnym kandydatem do buforowania. Przepisy w książce kucharskiej rzadko się zmieniają, jeśli w ogóle. Ponadto, ponieważ przeglądanie książek kucharskich jest głównym tematem aplikacji, API pobierające przepisy z pewnością będzie często wywoływane.

W poniższym przykładzie modyfikujemy funkcję widoku, aby używała buforowania. Po uruchomieniu funkcja sprawdza, czy klucz widoku znajduje się w pamięci podręcznej. Jeśli klucz istnieje, aplikacja pobiera dane z pamięci podręcznej i zwraca je. Jeśli nie, Django wysyła zapytanie do bazy danych, a następnie przechowuje wynik w pamięci podręcznej z kluczem widoku. Przy pierwszym uruchomieniu tej funkcji Django prześle zapytanie do bazy danych i wyrenderuje szablon, a następnie wykona wywołanie sieciowe do Redis w celu przechowania danych w pamięci podręcznej. Każde kolejne wywołanie funkcji całkowicie ominie bazę danych i logikę biznesową oraz prześle zapytanie do pamięci podręcznej Redis.

przykład/ustawienia.py

# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15

książka kucharska/views.py

from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from cookbook.services import get_recipes

CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)


@cache_page(CACHE_TTL)
def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

Zauważ, że dodaliśmy @cache_page() dekorator do funkcji widoku, wraz z czasem życia. Odwiedź /cookbook/ URL ponownie i sprawdź Django Debug Toolbar. Widzimy, że wykonywane są 3 zapytania do bazy danych i 3 wywołania do pamięci podręcznej w celu sprawdzenia klucza, a następnie jego zapisania. Django zapisuje dwa klucze (1 klucz dla nagłówka i 1 klucz dla wyrenderowanej zawartości strony). Odśwież stronę i obserwuj, jak zmienia się aktywność na stronie. Za drugim razem 0 wywołań do bazy danych i 2 wywołania do pamięci podręcznej. Nasza strona jest teraz obsługiwana z pamięci podręcznej!

Kiedy ponownie uruchamiamy nasze testy wydajności, widzimy, że nasza aplikacja ładuje się szybciej.

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

Buforowanie poprawiło całkowite obciążenie, a teraz rozwiązujemy 21 żądań na sekundę, czyli o 5 więcej niż w naszej linii bazowej:

Requests per second: 21


Inspekcja Redis za pomocą CLI

W tym momencie możemy użyć Redis CLI, aby sprawdzić, co jest przechowywane na serwerze Redis. W wierszu poleceń Redis wpisz keys * polecenie, które zwraca wszystkie klucze pasujące do dowolnego wzorca. Powinieneś zobaczyć klucz o nazwie „przykład:1:views.decorators.cache.cache_page”. Pamiętaj, że „przykład” to nasz prefiks klucza, „1” to wersja, a „views.decorators.cache.cache_page” to nazwa, którą Django nadaje kluczowi. Skopiuj nazwę klucza i wprowadź ją za pomocą get Komenda. Powinieneś zobaczyć wyrenderowany ciąg HTML.

$ redis-cli -n 1
127.0.0.1:6379[1]> keys *
1) "example:1:views.decorators.cache.cache_header"
2) "example:1:views.decorators.cache.cache_page"
127.0.0.1:6379[1]> get "example:1:views.decorators.cache.cache_page"

UWAGA: Uruchom flushall polecenie w interfejsie wiersza polecenia Redis, aby usunąć wszystkie klucze z magazynu danych. Następnie możesz ponownie wykonać kroki opisane w tym samouczku bez konieczności czekania na wygaśnięcie pamięci podręcznej.




Podsumowanie

Przetwarzanie żądań HTTP jest kosztowne, a koszty te zwiększają się wraz ze wzrostem popularności aplikacji. W niektórych przypadkach można znacznie zmniejszyć ilość przetwarzania, które wykonuje serwer, wdrażając buforowanie. Ten samouczek dotyczył podstaw buforowania w Django z Redis, ale tylko przejrzał powierzchnię złożonego tematu.

Implementacja pamięci podręcznej w solidnej aplikacji ma wiele pułapek i niedogodności. Kontrolowanie tego, co jest zapisywane w pamięci podręcznej i jak długo jest trudne. Unieważnienie pamięci podręcznej jest jedną z trudnych rzeczy w informatyce. Zapewnienie, że dostęp do prywatnych danych mają tylko zamierzeni użytkownicy, jest kwestią bezpieczeństwa i należy się z nim obchodzić bardzo ostrożnie podczas buforowania.

Bezpłatny bonus: Kliknij tutaj, aby uzyskać dostęp do bezpłatnego przewodnika po zasobach edukacyjnych Django (PDF), który zawiera wskazówki i triki, a także typowe pułapki, których należy unikać podczas tworzenia aplikacji internetowych Python + Django.

Baw się kodem źródłowym w przykładowej aplikacji i kontynuując rozwój z Django, pamiętaj, aby zawsze pamiętać o wydajności.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Migracja socket.io z 0.9.x do 1.x, problemy z konfiguracją RedisStore

  2. Jak wywołać funkcję po zakończeniu funkcji asynchronicznych w pętli?

  3. Połącz się z redis z innego kontenera w docker

  4. Udostępnianie gniazd w oddzielnych instancjach nodeJS

  5. Redis znajduje skróty według wartości pól