Die Anwendungsleistung ist entscheidend für den Erfolg Ihres Produkts. In einer Umgebung, in der Benutzer Reaktionszeiten von weniger als einer Sekunde erwarten, können die Folgen einer langsamen Anwendung in Dollar und Cent gemessen werden. Selbst wenn Sie nichts verkaufen, verbessern schnelle Seitenladevorgänge die Erfahrung beim Besuch Ihrer Website.
Alles, was auf dem Server zwischen dem Empfang einer Anfrage und dem Zurücksenden einer Antwort passiert, erhöht die Zeit, die zum Laden einer Seite benötigt wird. Als allgemeine Faustregel gilt:Je mehr Verarbeitung Sie auf dem Server eliminieren können, desto schneller wird Ihre Anwendung ausgeführt. Daten zwischenzuspeichern, nachdem sie verarbeitet wurden, und sie dann bei der nächsten Anforderung aus dem Cache bereitzustellen, ist eine Möglichkeit, den Server zu entlasten. In diesem Tutorial untersuchen wir einige der Faktoren, die Ihre Anwendung blockieren, und wir zeigen, wie Sie Caching mit Redis implementieren, um ihren Auswirkungen entgegenzuwirken.
Kostenloser Bonus: Klicken Sie hier, um Zugang zu einem kostenlosen Django Learning Resources Guide (PDF) zu erhalten, der Ihnen Tipps und Tricks sowie häufige Fallstricke zeigt, die Sie beim Erstellen von Python + Django-Webanwendungen vermeiden sollten.
Was ist Redis?
Redis ist ein In-Memory-Datenstrukturspeicher, der als Caching-Engine verwendet werden kann. Da es Daten im RAM speichert, kann Redis diese sehr schnell liefern. Redis ist nicht das einzige Produkt, das wir für das Caching verwenden können. Memcached ist ein weiteres beliebtes In-Memory-Caching-System, aber viele Leute sind sich einig, dass Redis Memcached in den meisten Fällen überlegen ist. Uns persönlich gefällt, wie einfach es ist, Redis für andere Zwecke wie Redis Queue einzurichten und zu verwenden.
Erste Schritte
Wir haben eine Beispielanwendung erstellt, um Ihnen das Konzept des Cachings vorzustellen. Unsere Anwendung verwendet:
- Django (v1.9.8)
- Django Debug Toolbar (v1.4)
- django-redis (v4.4.3)
- Redis (v3.2.0)
App installieren
Bevor Sie das Repository klonen, installieren Sie virtualenvwrapper, falls Sie es noch nicht haben. Dies ist ein Tool, mit dem Sie die spezifischen Python-Abhängigkeiten installieren können, die Ihr Projekt benötigt, sodass Sie die von Ihrer App benötigten Versionen und Bibliotheken isoliert ansprechen können.
Wechseln Sie als Nächstes in die Verzeichnisse, in denen Sie Projekte speichern, und klonen Sie das Beispiel-App-Repository. Wechseln Sie anschließend in das geklonte Repository und erstellen Sie dann mithilfe von mkvirtualenv
eine neue virtuelle Umgebung für die Beispiel-App Befehl:
$ mkvirtualenv django-redis
(django-redis)$
HINWEIS: Erstellen einer virtuellen Umgebung mit mkvirtualenv
aktiviert es auch.
Installieren Sie alle erforderlichen Python-Abhängigkeiten mit pip
, und checken Sie dann das folgende Tag aus:
(django-redis)$ git checkout tags/1
Schließen Sie die Einrichtung der Beispiel-App ab, indem Sie die Datenbank erstellen und mit Beispieldaten füllen. Stellen Sie sicher, dass Sie auch einen Superuser erstellen, damit Sie sich auf der Admin-Site anmelden können. Befolgen Sie die folgenden Codebeispiele und versuchen Sie dann, die App auszuführen, um sicherzustellen, dass sie ordnungsgemäß funktioniert. Besuchen Sie die Admin-Seite im Browser, um zu bestätigen, dass die Daten korrekt geladen wurden.
(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
Sobald Sie die Django-App ausgeführt haben, fahren Sie mit der Redis-Installation fort.
Redis installieren
Laden Sie Redis herunter und installieren Sie es gemäß den Anweisungen in der Dokumentation. Alternativ können Sie Redis mit einem Paketmanager wie apt-get installieren oder Homebrew je nach Betriebssystem.
Führen Sie den Redis-Server in einem neuen Terminalfenster aus.
$ redis-server
Starten Sie als Nächstes die Redis-Befehlszeilenschnittstelle (CLI) in einem anderen Terminalfenster und testen Sie, ob sie eine Verbindung zum Redis-Server herstellt. Wir werden die Redis-CLI verwenden, um die Schlüssel zu überprüfen, die wir dem Cache hinzufügen.
$ redis-cli ping
PONG
Redis stellt eine API mit verschiedenen Befehlen bereit, die ein Entwickler verwenden kann, um auf den Datenspeicher einzuwirken. Django verwendet django-redis um Befehle in Redis auszuführen.
Wenn wir unsere Beispiel-App in einem Texteditor betrachten, sehen wir die Redis-Konfiguration in der settings.py Datei. Wir definieren einen Standard-Cache mit den CACHES
Einstellung unter Verwendung eines integrierten django-redis Cache als unser Backend. Redis läuft standardmäßig auf Port 6379, und wir verweisen in unserer Einstellung auf diesen Ort. Eine letzte zu erwähnende Sache ist django-redis hängt Schlüsselnamen mit einem Präfix und einer Version an, um die Unterscheidung ähnlicher Schlüssel zu erleichtern. In diesem Fall haben wir das Präfix auf „Beispiel“ definiert.
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"
}
}
HINWEIS :Obwohl wir das Cache-Backend konfiguriert haben, hat keine der Ansichtsfunktionen Caching implementiert.
App-Leistung
Wie wir zu Beginn dieses Tutorials erwähnt haben, verlangsamt alles, was der Server tut, um eine Anfrage zu verarbeiten, die Ladezeit der Anwendung. Der Verarbeitungsaufwand für die Ausführung von Geschäftslogik und Renderingvorlagen kann erheblich sein. Die Netzwerklatenz wirkt sich auf die Zeit aus, die zum Abfragen einer Datenbank benötigt wird. Diese Faktoren kommen jedes Mal ins Spiel, wenn ein Client eine HTTP-Anforderung an den Server sendet. Wenn Benutzer viele Anfragen pro Sekunde initiieren, machen sich die Auswirkungen auf die Leistung bemerkbar, da der Server daran arbeitet, sie alle zu verarbeiten.
Wenn wir Caching implementieren, lassen wir den Server eine Anfrage einmal verarbeiten und speichern sie dann in unserem Cache. Wenn Anfragen für die gleiche URL von unserer Anwendung empfangen werden, zieht der Server die Ergebnisse aus dem Cache, anstatt sie jedes Mal neu zu verarbeiten. In der Regel legen wir eine Zeit fest, in der die zwischengespeicherten Ergebnisse gültig sind, damit die Daten regelmäßig aktualisiert werden können. Dies ist ein wichtiger Schritt, der implementiert werden muss, um zu vermeiden, dass veraltete Daten bereitgestellt werden.
Sie sollten in Betracht ziehen, das Ergebnis einer Anfrage zwischenzuspeichern, wenn die folgenden Fälle zutreffen:
- Das Rendern der Seite erfordert viele Datenbankabfragen und/oder Geschäftslogik
- die Seite wird häufig von Ihren Nutzern besucht,
- die Daten sind für jeden Benutzer gleich,
- und die Daten ändern sich nicht oft.
Beginnen Sie mit der Leistungsmessung
Beginnen Sie damit, die Geschwindigkeit jeder Seite in Ihrer Anwendung zu testen, indem Sie vergleichen, wie schnell Ihre Anwendung eine Antwort zurückgibt, nachdem sie eine Anfrage erhalten hat.
Um dies zu erreichen, werden wir jede Seite mit einem Burst von Anfragen unter Verwendung von loadtest, einem HTTP-Lastgenerator, bombardieren und dann genau auf die Anfragerate achten. Besuchen Sie den obigen Link, um zu installieren. Testen Sie nach der Installation die Ergebnisse anhand von /cookbook/
URL-Pfad:
$ loadtest -n 100 -k http://localhost:8000/cookbook/
Beachten Sie, dass wir etwa 16 Anfragen pro Sekunde verarbeiten:
Requests per second: 16
Wenn wir uns ansehen, was der Code macht, können wir Entscheidungen treffen, wie wir Änderungen vornehmen können, um die Leistung zu verbessern. Die Anwendung führt mit jeder Anforderung an /cookbook/
3 Netzwerkaufrufe an eine Datenbank durch , und es dauert bei jedem Aufruf einige Zeit, eine Verbindung herzustellen und eine Abfrage auszuführen. Besuchen Sie das /cookbook/
URL in Ihrem Browser und erweitern Sie die Registerkarte Django Debug Toolbar, um dieses Verhalten zu bestätigen. Suchen Sie das Menü mit der Bezeichnung „SQL“ und lesen Sie die Anzahl der Abfragen ab:
cookbook/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'))
cookbook/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()
})
Die Anwendung rendert auch eine Vorlage mit möglicherweise teurer 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>
Caching implementieren
Stellen Sie sich die Gesamtzahl der Netzwerkanrufe vor, die unsere Anwendung tätigen wird, wenn Benutzer beginnen, unsere Website zu besuchen. Wenn 1.000 Benutzer auf die API zugreifen, die Kochbuchrezepte abruft, fragt unsere Anwendung die Datenbank 3.000 Mal ab, und bei jeder Anfrage wird eine neue Vorlage gerendert. Diese Zahl wächst nur, wenn unsere Anwendung skaliert. Glücklicherweise ist diese Ansicht ein großartiger Kandidat für das Caching. Die Rezepte in einem Kochbuch ändern sich selten, wenn überhaupt. Da das Anzeigen von Kochbüchern das zentrale Thema der App ist, wird die API, die die Rezepte abruft, garantiert häufig aufgerufen.
Im folgenden Beispiel ändern wir die Ansichtsfunktion, um Caching zu verwenden. Wenn die Funktion ausgeführt wird, prüft sie, ob sich der Ansichtsschlüssel im Cache befindet. Wenn der Schlüssel vorhanden ist, ruft die App die Daten aus dem Cache ab und gibt sie zurück. Wenn nicht, fragt Django die Datenbank ab und legt das Ergebnis dann mit dem Ansichtsschlüssel im Cache ab. Wenn diese Funktion zum ersten Mal ausgeführt wird, fragt Django die Datenbank ab und rendert die Vorlage und führt dann auch einen Netzwerkaufruf an Redis durch, um die Daten im Cache zu speichern. Jeder nachfolgende Aufruf der Funktion umgeht die Datenbank und die Geschäftslogik vollständig und fragt den Redis-Cache ab.
Beispiel/settings.py
# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15
cookbook/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()
})
Beachten Sie, dass wir @cache_page()
hinzugefügt haben decorator zur Ansichtsfunktion, zusammen mit einer Zeit zu leben. Besuchen Sie das /cookbook/
URL erneut und untersuchen Sie die Django Debug Toolbar. Wir sehen, dass 3 Datenbankabfragen und 3 Aufrufe an den Cache erfolgen, um nach dem Schlüssel zu suchen und ihn dann zu speichern. Django speichert zwei Schlüssel (1 Schlüssel für den Header und 1 Schlüssel für den gerenderten Seiteninhalt). Laden Sie die Seite neu und beobachten Sie, wie sich die Seitenaktivität ändert. Beim zweiten Mal werden 0 Aufrufe an die Datenbank und 2 Aufrufe an den Cache getätigt. Unsere Seite wird jetzt aus dem Cache bedient!
Wenn wir unsere Leistungstests erneut ausführen, stellen wir fest, dass unsere Anwendung schneller geladen wird.
$ loadtest -n 100 -k http://localhost:8000/cookbook/
Caching hat die Gesamtlast verbessert und wir lösen jetzt 21 Anfragen pro Sekunde, das sind 5 mehr als unser Ausgangswert:
Requests per second: 21
Untersuchen von Redis mit der CLI
An dieser Stelle können wir die Redis-CLI verwenden, um zu sehen, was auf dem Redis-Server gespeichert wird. Geben Sie in der Redis-Befehlszeile die keys *
ein -Befehl, der alle Schlüssel zurückgibt, die einem beliebigen Muster entsprechen. Sie sollten einen Schlüssel namens „example:1:views.decorators.cache.cache_page“ sehen. Denken Sie daran, „example“ ist unser Schlüsselpräfix, „1“ ist die Version und „views.decorators.cache.cache_page“ ist der Name, den Django dem Schlüssel gibt. Kopieren Sie den Schlüsselnamen und geben Sie ihn mit get
ein Befehl. Sie sollten den gerenderten HTML-String sehen.
$ 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"
HINWEIS: Führen Sie flushall
aus Befehl auf der Redis-CLI, um alle Schlüssel aus dem Datenspeicher zu löschen. Anschließend können Sie die Schritte in diesem Tutorial erneut ausführen, ohne warten zu müssen, bis der Cache abgelaufen ist.
Zusammenfassung
Die Verarbeitung von HTTP-Anforderungen ist kostspielig, und diese Kosten summieren sich, wenn Ihre Anwendung immer beliebter wird. In einigen Fällen können Sie den Verarbeitungsaufwand Ihres Servers erheblich reduzieren, indem Sie Caching implementieren. Dieses Tutorial hat die Grundlagen des Cachings in Django mit Redis angesprochen, aber es hat nur die Oberfläche eines komplexen Themas überflogen.
Das Implementieren von Caching in einer robusten Anwendung hat viele Fallstricke und Fallstricke. Es ist schwierig zu kontrollieren, was zwischengespeichert wird und wie lange. Cache-Invalidierung ist eines der schwierigsten Dinge in der Informatik. Sicherzustellen, dass nur die vorgesehenen Benutzer auf private Daten zugreifen können, ist ein Sicherheitsproblem und muss beim Caching sehr sorgfältig gehandhabt werden.
Kostenloser Bonus: Klicken Sie hier, um Zugang zu einem kostenlosen Django Learning Resources Guide (PDF) zu erhalten, der Ihnen Tipps und Tricks sowie häufige Fallstricke zeigt, die Sie beim Erstellen von Python + Django-Webanwendungen vermeiden sollten.
Spielen Sie mit dem Quellcode in der Beispielanwendung herum, und denken Sie bei der weiteren Entwicklung mit Django daran, die Leistung immer im Auge zu behalten.