Lebensdauer von Objekten im RAM, Threadsicherheit, Prozesse

Django, Flask, Bottle, WSGI, CGI…
Antworten
doppelkeks
User
Beiträge: 12
Registriert: Sonntag 14. Februar 2010, 22:28

Ich bin noch neu in der schillenden Welt der Webentwicklung, und weiß teilweise noch nicht, welche Konzepte aus der Programmierung üblicher lokaler Programme auf die Webprogrammierung übertragbar sind, und welche nicht.
Ich kann auch trotz Google einige Fragen nicht abschliessend klären:

1) Wann leben Objekte nur für die Dauer eines Requests (werden also immer wieder neu instanziiert), und unter welchen Umständen überdauern Objekte die gesamte Server Uptime?
Wenn ich eine Reihe von Objekten habe, deren Konstruktion aufwendig ist, wie mach ich es, dass die nur einmal gebaut werden müssen? Ich denke hier z.B. an dynamisch erzeugte Modelle.
Kann ich eine Datenstruktur dynamisch generieren und über die gesamte Laufzeit des Servers im Speicher halten, und bei allen Requests oder Modellmethoden darauf zugreifen?
Ich denke mal, Objekte, die in einem Modul direkt instanziiert werden, sind persistent?

2) Konkurrenz. Ist Threadsicherheit ausreichend? Oder hängt das vom verwendeten Server ab? Apache scheint sowohl Threads als auch Forks zu nutzen? Wie geh ich damit um, wenn es sich nicht über die Datenbank machen lässt? Beispiel wäre schreibender Zugriff auf das Dateisystem, oder im RAM gehaltene Zustände. Würde es Sinn machen, im Settings Modul einen Prozess zu starten, der Zustände und Jobqueues verwaltet?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Zu 1) Objekte werden prinzipiell irgendwann gelöscht nach dem alle Referenzen gelöscht wurden. Wie lange dass konkret dauert hängt vom Interpreter ab, bei CPython sofort bei anderen Implementationen dauert es eine Weile, daher sollte man sich unter keinen Umständen darauf verlassen.

Ob du auf eine Datenstruktur in allen Requests zugreifen kannst hängt vom Deployment ab. Es gibt Server die Greenlets und Threads nutzen so dass du dann anfangen müsstest Locking zu betreiben es sei den du implementierst threadsichere Datenstrukturen die dies nicht erfordern.

Wenn jemand die Anwendung im grösseren Stil nutzt und mehrere Anwendungsserver verwendet ist es schlichtweg unmöglich auf diese globale Datenstruktur zuzugreifen. Du könntest allerdings die Datenstruktur in einem seperaten Prozess halten und mit diesem über ZeroMQ, ein REST-Interface oder ähnliches kommunizieren.

Zu 2) Threadsicherheit alleine reicht nicht unbedingt aus; nutzt der Server Greenlets und Threads, machen eventuell Greenlet Locks Sinn um die Performance zu erhöhen; du musst aber auch mit Forks oder Prozessen rechnen.

Schreibenden Zugriff auf das Dateisystem erreichst du indem du sicherstellst dass du immer neue Dateien erzeugst die nie eine bestehende überschreiben und/oder in dem du Locks und atomare Operationen nutzt. Das ist noch deutlich aufwendiger als es ohnehin schon klingt und platformabhängig.

Eine Jobqueue macht Sinn, dafür würde ich aber auf celery zurückgreifen.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

doppelkeks hat geschrieben:1) Wann leben Objekte ...
Eigentlich alle Web-Rahmenwerke bei Python folgen der WSGI-Spezifikation, die wie ihr Vorbild CGI den Request ins Zentrum stellt. Typischerweise wird eine Funktion bei einem Request aufgerufen, verarbeitet diesen, erstellt eine Response und das war's dann. Ich würde daher davon ausgehen, dass alle Objekte nur so lange leben, wie diese Verarbeitung dauert. Das Konzept eines Application Servers wie bei Java, der neben einem Request-Context noch einen Session- und Application-Context zur Verfügung stellt, gibt es normalerweise nicht. Spezielle WSGI-Server könnten zusätzliche Garantieren geben, aber WSGI selbst hat hier keine Antwort.
doppelkeks hat geschrieben:2) Konkurrenz. Ist Threadsicherheit ausreichend?
So pauschal gefragt muss die Antwort nein lauten. Das korrekte Funktionen auch bei nebenläufigem Zugriff muss die Anwendung selbst sicherstellen. Python bietet dazu eine Standardbibliothek mit Locks. Datenbanken bieten in der Regel Transaktionen oder zumindest Atomizität (falls das ein Wort ist ;). Ob sich zwei Funktionen, die zwei Requests bearbeiten, überhaupt Speicher teilen, d.h. im selben Python-Interpreter ablaufen, ist AFAIK bei WSGI nicht definiert. Das hängt also vom konkreten Server ab.

Stefan
doppelkeks
User
Beiträge: 12
Registriert: Sonntag 14. Februar 2010, 22:28

Ok, danke, ich denke damit bin ich ein stück weiter.
Ist aber schon etwas schade, dass man bei WSGI auf den Request Kontext beschränkt bleibt, wobei das bei den meissten Applikationen wohl ziemlich wurst ist.
deets

doppelkeks hat geschrieben:Ok, danke, ich denke damit bin ich ein stück weiter.
Ist aber schon etwas schade, dass man bei WSGI auf den Request Kontext beschränkt bleibt, wobei das bei den meissten Applikationen wohl ziemlich wurst ist.
Das ist auch schlicht nicht wahr. WSGI macht ueberhaupt keine Aussagen darueber. Und alle praktisch mir bekannten WSGI-Server sind stateful. Das liegt schon alleine darin begruendet, dass die setup-Kosten in Python hoeher als in zB PHP sind. Bis bei unseren Servern (mod_wsgi-basiert) nach einem Neustart ein Prozess da ist, vergehen schon ein paar Sekunden. Das kann & will man sich ja nicht jedes Request zumuten.

Man muss natuerlich aufpassen, wenn man globalen Zustand benutzt - nur lesen eben. Aber genau dafuer (Caches usw) ist er ja auch gedacht. Ausserdem ist multi-threading in Python ja so ein bisschen problematisch. Eine Webanwendung, die datenbanklastig ist, sollte damit kein Problem haben. Da gibt es genug Wartezyklen, in denen andere Threads laufen. Aber man sollte schon auf Prozesse abzielen.
BlackJack

@deets: Aber muss man nicht auch immer den "worst case" im Auge haben, dass man eine Anwendung gegen WSGI schreibt und sich um das konkrete Deployment erst einmal keine Gedanken macht und am Ende fest stellt, das man WSGI über CGI laufen lassen muss?
deets

@BlackJack

Halte ich fuer esoterisch, aber wenn das deine Randbedingungen sein sollten (und du kein fastCGI benutzen kannst, das ja aus genau dem Grund existiert), dann hast du halt uU ein Problem. Aber das hast du dann mit so ziemlich allem, was du so an Webframeworks findest. Natuerlich wird dir bottle da entgegenkommen, aber schon meine Datenbankverbindungen habe ich ganz gerne gepoolt, und nicht jedes mal aufgebaut. Und wie gesagt, alleine schon der import von diversen dutzend Modulen dauert seine Zeit.

Und es ging ja auch um die Aussage, ob WSGI nun *erzwingt*, dass alles nur einen Request-Zyklus lang lebt. Und das stimmt nicht. Das haben weder sma noch DasIch behauptet, aber beim OP scheint's so angekommen zu sein.
BlackJack

@deets: Hm, finde ich gar nicht so esoterisch. Es gibt nun einmal Hoster die ältere Python-Versionen mit CGI anbieten und es gibt Leute die können oder wollen nicht den Hoster wechseln.
lunar

@deets: Auf der anderen Seite garantiert WSGI eben auch nicht, dass irgendetwas über den Request-Zyklus hinaus lebt. Man kann folglich keine Informationen über den Programmzustand austauschen, sondern muss den Anwendungszustand (e.g. die Session) irgendwie extern speichern. Das gilt ja auch, wenn man persistente Prozesse hat, weil man a priori nicht weiß, bei welchem Prozess ein Request letztlich landet.

Das zu wissen, ist imho viel wichtiger, weil es direkten Einfluss hat darauf, wie man die Anwendungslogik implementiert. Insofern ist es eigentlich gut, wenn der OP erst einmal davon ausgeht, dass der Prozess nur während des Request-Zyklus lebt.

Pooling und Caching ist für den OP doch erstmal eh irrelevant, und wird zumindest im Bezug auf die Datenbankverbindungen eh vom Rahmenwerk übernommen.
deets

BlackJack hat geschrieben:@deets: Hm, finde ich gar nicht so esoterisch. Es gibt nun einmal Hoster die ältere Python-Versionen mit CGI anbieten und es gibt Leute die können oder wollen nicht den Hoster wechseln.
Klar, aber man kann das doch nicht zur Praemisse fuer alle Entwicklung machen. "Ich koennte uU irgendwann mal wo laufen, wo ich dies, das und jenes alles nicht habe." Dann kannst du genauso nix > Python2.3 vorraussetzen usw, weil es irgendwo irgendwen gibt, der eben nur das anbietet.

Ich habe im uebrigen ja auch nicht behauptet, dass man WSGI so nicht einsetzen kann. Aber die boot-strap-Zeit ist nunmal da, das kann man ja nicht wegdiskutieren.
BlackJack

@deets: Aber auch ein aktuelles Python garantiert nichts anderes, weil WSGI das eben nicht garantiert. Mit Deiner Argumentation könnte man auch sagen man muss Dateien nicht explizit schliessen, die werden ja in CPython zeitnah automatisch geschlossen. Alles andere ist eher esoterisch.
deets

@BlackJack

Du hast natuerlich Recht, das WSGI nix garantiert. Aber es geht doch darum, was fuer Moeglichkeiten man hat bei der Entwicklung einer Webanwendung. Und dabei ist es gaengig, auch bei WSGI-Anwendungen davon auszugehen, dass man langlebige Prozesse hat. Die Technik ist da, es spricht auch nichts dagegen, es zu tun. Faktisch muss man sich schon bemuehen, es nicht zu haben, da zumindest die mir bekannten deployment-Szenarien (mod_wsgi/mod_python, proxying) alle langlebige Prozesse haben. CGI-nach-WSGI - wie macht man das? Also, dass es geht ist mir klar, aber ist das ein Standard-Weg? Ich habe nur mal mit flup zu tun gehabt, und das war schon fastCGI (sonst haette es auch schlicht nicht funktioniert)

Anders gefragt: kennst du eine Installation mit einem der komplexeren Frameworks, die via CGI (nicht fastCGI) laeuft?
BlackJack

@deets: Für Django gibt es zum Beispiel dieses CGI-Skript, das im Netz viel verlinkt wird von Anleitungen wie man Django auf Sharedhostern mit CGI laufen lassen kann: http://code.djangoproject.com/attachmen ... django.cgi

Und `wsgiref` aus der Standardbibliothek bietet das hier: wsgiref.handlers.CGIHandler
deets

@BlackJack

Und in dem Skript steht:

"""
This is probably the slowest way to serve django pages, as the python
interpreter, the django code-base and your site code has to be loaded every
time a request is served. FCGI and mod_python solve this problem, use them if
you can.
"""
BlackJack

@deets: Wenn es die einzige Möglichkeit ist, dann ist das doch wurscht, dass es die langsamste wäre, wenn man denn andere zur Verfügung hätte.

Wenn sich jemand für einen Hoster entscheiden soll, dann würde ich natürlich auch sagen, nimm bloss keinen der nur CGI bietet wenn Du eine Webanwendung darauf laufen lassen willst.
deets

@BlackJack

Irgendwie ist mir der Sinn dieser ganzen Diskussion nicht ersichtlich. WSGI macht keine Aussage ueber die Lebensdauer eines Prozesses. Soviel ist klar. Wieso du jetzt aber darauf beharrst, dass man bei der Entwicklung ja das Szenario "aber es koennte nur CGI sein!" beachten sollte (als eine sehr harte & IMHO unuebliche Beschraenkung), aber andere Einschraenkungen bezueglich zB der zu verwendenden Python-Version, 3rd-party Packages oder sonstigen (AppEngine & seine EInschraenkungen bzgl. Datenbank sowie File-Count zB) irrelevant waeren, verstehe ich nicht.

Entweder diskutieren wir hier allgemeine Vorgehensweisen & Moeglichkeiten beim Web-App-Design. Und da sind langlebige Prozesse moeglich, wie es der OP gerne haette (und sie auch unzweifelhaft sinnvoll sind zum Aufbau von Caches aller Arten, und sei's nur der geladenen Module).

Oder ein konkretes "ich bin bei BilligHosterXY, was kann ich tun". Dann faellt unter Umstaenden aber auch so manch anderes ueber Bord.
doppelkeks
User
Beiträge: 12
Registriert: Sonntag 14. Februar 2010, 22:28

deets hat geschrieben:Und es ging ja auch um die Aussage, ob WSGI nun *erzwingt*, dass alles nur einen Request-Zyklus lang lebt. Und das stimmt nicht. Das haben weder sma noch DasIch behauptet, aber beim OP scheint's so angekommen zu sein.
Na, ich habs so verstanden, dass es eben einfach nicht definiert ist; Mit anderen Worten also: "Kann sein, oda ooch net", man kann sich nicht drauf verlassen, was dann für Determinismus-Fetischisten soviel wie "nur Request-Kontext" bedeutet.

Ist denn der global Zustand in einer WSGI Umgebung aus Programmierer Sicht das Selbe wie globale Variablen in Python? Oder sind alle auf Modul-Ebene instanziierten Objekte für die Server-Prozesse global?
Wenn ich das, was ich vorhin in einem Blog über Djangos (brandneue) View-klassen gelesen habe richtig verstehe, dann wird so eine Klasse nur einmal instanziiert, nicht jedes Mal pro Request? Was dann auch bedeutet, dass jede setter methode ohne locking eine race condition darstellt?
deets

doppelkeks hat geschrieben: Ist denn der global Zustand in einer WSGI Umgebung aus Programmierer Sicht das Selbe wie globale Variablen in Python? Oder sind alle auf Modul-Ebene instanziierten Objekte für die Server-Prozesse global?
Wenn ich das, was ich vorhin in einem Blog über Djangos (brandneue) View-klassen gelesen habe richtig verstehe, dann wird so eine Klasse nur einmal instanziiert, nicht jedes Mal pro Request? Was dann auch bedeutet, dass jede setter methode ohne locking eine race condition darstellt?
Es sind globale Variablen im Python-Sinn - also alles Modul-Global, ausser du gibst dir besondere Muehe und stopfst etwas in die builtins (was technisch ja auch "nur" ein Modul ist)

Ich kenne die views von django nicht, aber in TurboGears gibt's "Widgets" - genauer, ToscaWidgets. Und die haben einen globalen Teil, und einen Per-Request-Teil. Ersterer definiert Zustand, der sich nicht aendert - zB Templates, statische Resourcen (CSS, Skripte) und uU auch Konfiguration wie zB die action einer Form.

Und im Request werden dann Dinge wie zB Formularwerte oder konkrete Parameter bestimmt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Views in Django sind insofern global dass sie idR Funktionen in einem Modul sind, die zur Zeit definiert werden wo das Modul geladen wird. Nicht mehr, nicht weniger. Wenn das Modul nicht neu geladen wird, bleiben die Views geladen, aber ausgeführt werden sie bei jedem Request neu (außer natürlich man hängt sich etwa mit einer Middleware davor und hält die ab, etwa durch einen Cache, aber dann ist das ja sowieso klar).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
doppelkeks
User
Beiträge: 12
Registriert: Sonntag 14. Februar 2010, 22:28

ok danke, dann ist mir jetzt klar.
Antworten