Skalierbare Webanwendungen mit Python und Google App Engine Oliver Albers 03. Juli 2008 1/32
Einführung Worum geht es? Pro und Contra Technik Genereller Aufbau Anwendungskonfiguration Verarbeitung von Requests URL Mapping Templates Datastore API GQL Skalierbarkeit Authentifizierung 2/32
Worum geht es? Wir beschäftigen uns mit der Implementierung von Webanwendungen. Hoffentlich viele Benutzer Performance ist K.O.-Kriterium für Akzeptanz Skalierbarkeit und Hochverfügbarkeit Einführung 3/32
Worum geht es? Kaum ein Unternehmen hat hier mehr Erfahrung als Google Wir lernen hier die Entwicklung mit von Google bereitgestellten Tools Wir können die Anwendungen dann in Google-Rechenzentren hosten Einführung 4/32
Vorteile? Kostengünstige Lösung Profitieren von Googles Erfahrung Einführung 5/32
Nachteile? Datenschutz Vendor Lock-In Beta Einführung 6/32
Genereller Aufbau Was ist App Engine? Sammlung von Python-Bibliotheken, die auf den Servern bereitstehen Hosting-Dienstleistung für Anwendungen SDK für Entwickler http://code.google.com/appengine/ Technik 7/32
Genereller Aufbau Python und dessen Bibliotheken wurden für diese Zwecke angepasst, es können nicht alle Features / Bibliotheken verwendet werden, z.b. Keine Sockets Keine Threads Keine Systemcalls Keine Kindprozesse, daher einige Methoden aus os nicht vorhanden Technik 8/32
Anwendungskonfiguration Die Anwendung muss für das Deployment und den Entwicklungsserver konfiguriert werden Konfiguration über yaml Datei app.yaml im Hauptverzeichnis der Anwendung: a p p l i c a t i o n : r a n d r v e r s i o n : 1 r u n t i m e : python a p i v e r s i o n : 1... Technik 9/32
Handler In der app.yaml können dann auch gleich URL-Handler konfiguriert werden. a p p l i c a t i o n : r a n d r v e r s i o n : 1 r u n t i m e : python a p i v e r s i o n : 1 h a n d l e r s : u r l : /. s c r i p t : i n d e x. py Technik 10/32
Verarbeitung von Requests Alle Zugriffe werden nun auf unsere index.py gemappt, diese muss die Requests verarbeiten Dafür ist in Python das WSGI 1 zuständig Das SDK bringt bereits ein einfaches Framework dafür mit 1 Web Server gateway Interface Technik 11/32
Einfache index.py import w s g i r e f. h a n d l e r s from g o o g l e. a p p e n g i n e. e x t import webapp c l a s s I n d e x ( webapp. R e q u e s t H a n d l e r ) : d e f g e t ( s e l f ) : s e l f. r e s p o n s e. h e a d e r s [ Content Type ] = t e x t / p l a i n s e l f. r e s p o n s e. out. w r i t e ( H a l l o Welt! ) d e f main ( ) : a p p l i c a t i o n = webapp. WSGIApplication ( [ ( /, I n d e x ) ], debug=true ) w s g i r e f. h a n d l e r s. CGIHandler ( ). run ( a p p l i c a t i o n ) i f n a m e == m a i n : main ( ) Technik 12/32
Verarbeitung von Requests Requests werden vom Hosting an unsere index.py weitergereicht index.py erzeugt eine WSGI-Anwendung Diese Anwendung leitet Requests an Instanzen der angegebenen Klasse (Kind von RequestHandler) weiter In diesen Instanzen wird passend zum Request get, post, put, head,... ausgeführt Technik 13/32
Formularverarbeitung An den Request übergebene Parameter sind im RequestHandler-Objekt verfügbar Dabei wird nicht zwischen get und post unterschieden us einem Formular z.b. der Wert id übergeben ist er wie folgt erreichbar: d e f p o s t ( s e l f ) : p r i n t s e l f. r e q u e s t. g e t ( i d ) Technik 14/32
URL Mapping In unserer Anwendung können wir URLs nochmals verfeinert auf Klassen mappen a p p l i c a t i o n = webapp. WSGIApplication ( [ ( /, I n d e x ), ( / number /, NumberPage ), ( /home, I n d e x ) ], debug=true ) Es werden auch reguläre Ausdrücke unterstützt ( r /home /., HomePage ) Technik 15/32
URL Mapping Matches aus dem regulären Ausdruck können direkt als Parameter der get-methode übergeben werden ( r /home / (. ), HomePage )... d e f g e t ( s e l f, param ) : p r i n t param Technik 16/32
Templates HTML Code in den Klassen zu erzeugen ist mühselig, daher Templates Das SDK liefert Django 2 für Templates mit MVC: Python-Klassen als Controller, Templates als View-Schicht 2 http://www.djangoproject.com/ Technik 17/32
Templates index.py: d e f g e t ( s e l f ) : t e m p l a t e v a r s = { t e x t : H a l l o Welt! } path = os. path. j o i n ( os. path. dirname ( f i l e ), t e m p l a t e. html ) s e l f. r e s p o n s e. out. w r i t e ( t e m p l a t e. r e n d e r ( path, t e m p l a t e v a r s ) ) template.html: <html><body> <p> {{ t e x t }}</p> </body></html> Technik 18/32
Statische Files Man muss aber auch statische Files (z.b. JavaScript-Files, CSS, Bilder) ausliefern können Definieren eines Verzeichnisses für statische Dateien Konfigurieren eines URL-Handlers für dieses Verzeichnis in der app.yaml: h a n d l e r s : u r l : / f i l e s s t a t i c d i r : f i l e s u r l : /. s c r i p t : i n d e x. py Technik 19/32
Datastore App Engine bietet keine relationale Datenbank Persistente Speicherung von Daten über den Datastore Datastore 20/32
Datastore Verteiles System zur Datenspeicherung Vergleichbar mit Distributed Hashtable Datastore 21/32
API Datastore arbeitet nicht mit Tabellen, sondern Objekten Model-Schicht der Anwendung direkt auf Datastore abbildbar Entities müssen aber direkt auf die API angepasst sein Datastore 22/32
API from g o o g l e. a p p e n g i n e. e x t import db from g o o g l e. a p p e n g i n e. a p i import u s e r s c l a s s Pet ( db. Model ) : name = db. S t r i n g P r o p e r t y ( r e q u i r e d=true ) t y p e = db. S t r i n g P r o p e r t y ( c h o i c e s=s e t ( [ c a t, dog, b i r d ] ) ) b i r t h d a t e = db. D a t e P r o p e r t y ( ) w e i g h t i n p o u n d s = db. I n t e g e r P r o p e r t y ( ) s p a y e d o r n e u t e r e d = db. B o o l e a n P r o p e r t y ( ) owner = db. U s e r P r o p e r t y ( ) p e t = Pet ( name= F l u f f y, t y p e= c a t, owner=u s e r s. g e t c u r r e n t u s e r ( ) ) p e t. w e i g h t i n p o u n d s = 24 p e t. put ( ) Datastore 23/32
GQL Objekte können mittels GQL aus Datastore abgefragt werden Für einfache Abfragen werden automatisch Indizes für abgefragte Eigenschaften erzeugt p e t s = Pet. g q l ( WHERE w e i g h t i n p o u n d s > : 1, 20) f o r p e t i n p e t s p r i n t p e t s [ p e t ]. name Datastore 24/32
GQL Ist nicht SQL Unterstützte WHERE-Conditions:, =,, =, =,!=, IN, ANCESTOR IS ORDER BY ist unterstützt Keine Joins Datastore 25/32
Skalierbarkeit Lesende Zugriffe auf den Datastore sind sehr schnell Schreibzugriffe in den Datastore garantieren Konsistenz Müssen auf Festplatte warten langsam! Möglichst nicht aus vielen Requests das gleiche Objekt beschreiben, Vermeidung durch Anpassung der Datenstruktur Datastore 26/32
Beispiel: Besucherzähler Jeder Request muss Objekt laden, ändern, speichern Obere Grenze für Skalierbarkeit also Speichergeschwindigkeit für Objekt Datastore 27/32
Beispiel: Besucherzähler Verwenden von n verschiedenen Counter-Objekten Jeder Request erhöht nur einen dieser n Counter, n * Speichergeschwindigkeit Zählerstand ist dann n i=1 Counter i Schnell auszulesen Weiter Optimierbar durch die memcache-api Datastore 28/32
Benutzer Das SDK bringt auch eine API für Benutzerauthentifizierung mit Benutzer als Property-Typ für Datastore-Model vorhanden Einfach zu benutzen Authentifizierung 29/32
Benutzer from g o o g l e. a p p e n g i n e. a p i import u s e r s d e f g e t ( s e l f ) : u s e r = u s e r s. g e t c u r r e n t u s e r ( ) i f u s e r i s None : u r i = u s e r s. c r e a t e l o g i n u r l ( s e l f. r e q u e s t. u r i ) s e l f. r e d i r e c t ( u r i, F a l s e ) r e t u r n Authentifizierung 30/32
Benutzer Benutzer-Objekte jedoch schwierig für nicht eingeloggten Benutzer zu instantiieren Authentifizierung nur gegen Google-Accounts Authentifizierung 31/32
Vielen Dank! Authentifizierung 32/32