Auslesen einer Datenbank und halten der Daten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Transmitter
User
Beiträge: 24
Registriert: Mittwoch 24. November 2004, 02:23

Hi

Ich bin ganz neu bei Python, gefällt mir aber bisher sehr gut.

Ich schreibe gerade an einem Online-Browser-Game in PHP.
Um die Datenbank zu entlasen würde ich gewisse Sachen gerne von Python machen lassen.

Mit Twisted habe ich mir jetzt mal zum Testen einen kleinen XMLRPC Server geschrieben, über diesen soll die Benutzer ID an Python übergeben werden.

Dann ließt Python sämtliche Daten des Benutzers aus der Datenbank und hält sie im Speicher.

Wie sollte das denn funktionieren?
Objektorientiert?
Ist das auch noch in Ordnung, wenn sich 5000 Benutzer anmelden und 5000 Instanzen der Klasse Spieler im Speicher liegen?

Nach 10 Minuten sollen die neuen Daten des Benutzers ( Veränderte Summer an Geld wegen Handel / Kauf / Verdienst usw. ) wieder in die Datenbank geschrieben werden.

Muss ich dann eine while ( true ) Schleife laufen lassen:
if ( ( now - 10min ) > lastUpdate )
updateDataBase
oder gibt es eine bessere Möglichkeit dafür?

Falls der Spieler länger als x Min nicht mehr aktiv war, soll die Instanz auch in die DB geschrieben werden und gelöscht werden.
Gibt es in Python auch so etwas wie einen GarbageCollector?
Oder muss man hier einen Destruktor aufrufen und selbst den Speicher freigeben?

Denke mal, pygame wird nicht das richtige für mich sein, da ich ja mehr einen Persistenzcontainer mit Updatefeatures bauen will, kein eigenständiges Spiel.

Hoffe, ihr könnt mir helfen.

Bye, Transmitter
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Transmitter hat geschrieben:Hi
Hi auch und willkommen im Pythonforum,
Transmitter hat geschrieben: Ich bin ganz neu bei Python, gefällt mir aber bisher sehr gut.
Ich zeige schon erste Gebrauchsspuren, aber Python gefällt mir immer noch sehr gut ;)
Transmitter hat geschrieben: Ich schreibe gerade an einem Online-Browser-Game in PHP.
Um die Datenbank zu entlasen würde ich gewisse Sachen gerne von Python machen lassen.

Mit Twisted habe ich mir jetzt mal zum Testen einen kleinen XMLRPC Server geschrieben, über diesen soll die Benutzer ID an Python übergeben werden.

Dann ließt Python sämtliche Daten des Benutzers aus der Datenbank und hält sie im Speicher.

Wie sollte das denn funktionieren?
Objektorientiert?
Ja wird wohl auf eine objektorientierte Lösung mit einem Dictionary hinauslaufen.
Transmitter hat geschrieben: Ist das auch noch in Ordnung, wenn sich 5000 Benutzer anmelden und 5000 Instanzen der Klasse Spieler im Speicher liegen?
solange der Speicher reicht sollte es kein Problem geben.
Transmitter hat geschrieben: Nach 10 Minuten sollen die neuen Daten des Benutzers ( Veränderte Summer an Geld wegen Handel / Kauf / Verdienst usw. ) wieder in die Datenbank geschrieben werden.

Muss ich dann eine while ( true ) Schleife laufen lassen:
if ( ( now - 10min ) > lastUpdate )
updateDataBase
oder gibt es eine bessere Möglichkeit dafür?
Das was du meinst nennt man "busywaiting" und wird bei Multitasking-Betriebssystemen mit Linuxverbot nicht unter 10 Jahren geahndet ;)
Besser macht man das mit einem Scheduler, der z.b. alle Minute oder alle 10 Minuten aufgerufen wird und nachschaut ob Daten zu sichern sind oder nicht. Der kann auch in einem eigenen Thread laufen, dann ist das Hauptprogramm immer noch in der Lage auf Anforderungen (neuer User loggt sich ein o.Ä.) zu reagieren.
Transmitter hat geschrieben: Falls der Spieler länger als x Min nicht mehr aktiv war, soll die Instanz auch in die DB geschrieben werden und gelöscht werden.
Gibt es in Python auch so etwas wie einen GarbageCollector?
Oder muss man hier einen Destruktor aufrufen und selbst den Speicher freigeben?
Jo Python hat auch einen GarbageCollector und verwaltet den Speicher den Objekte belegen selbstständig. Sobald ein Objekt nicht mehr benötigt wird, d. h. wenn keine Referenz mehr auf das Objekt existiert wird dessen Destruktor automatisch aufgerufen und der belegte Speicher freigegeben.
Der eigentliche GarbageCollector ist dann für zyklische Referenzen zuständig.
Transmitter hat geschrieben: Denke mal, pygame wird nicht das richtige für mich sein, da ich ja mehr einen Persistenzcontainer mit Updatefeatures bauen will, kein eigenständiges Spiel.
Jo mit Pygame hat das nix zu tun.
Transmitter hat geschrieben: Hoffe, ihr könnt mir helfen.

Bye, Transmitter
Hoffe etwas geholfen zu haben :)


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Transmitter
User
Beiträge: 24
Registriert: Mittwoch 24. November 2004, 02:23

Dookie hat geschrieben: Hi auch und willkommen im Pythonforum,
Danke :)
Dookie hat geschrieben: Ja wird wohl auf eine objektorientierte Lösung mit einem Dictionary hinauslaufen.
Dictionary ist, wie ich in einem Buch lesen kann, eine nicht sortierte Liste von Objekten?
Also vergleichbar mit einer Collection in Java?

In dieser Collection haben dann alle Benutzerobjekte Platz, sie werden erzeugt, und mit ihren Daten in die Liste geschrieben?
Falls ich ein Objekt brauche, wird auf dieses Anhand eines Namens ( key ) zugegriffen?
Dookie hat geschrieben:
Transmitter hat geschrieben: 5000 Instanzen
solange der Speicher reicht sollte es kein Problem geben.
Kannst du grob abschätzen, wieviel sowas benötigt?
Oder kommt ich mit 1GB Ram für Python, Webserver UND MySQL DB hin?
Dookie hat geschrieben: Das was du meinst nennt man "busywaiting" und wird bei Multitasking-Betriebssystemen mit Linuxverbot nicht unter 10 Jahren geahndet ;)
Ja, sowas hatte ich befürchtet ;)
Dookie hat geschrieben: Besser macht man das mit einem Scheduler, der z.b. alle Minute oder alle 10 Minuten aufgerufen wird und nachschaut ob Daten zu sichern sind oder nicht. Der kann auch in einem eigenen Thread laufen, dann ist das Hauptprogramm immer noch in der Lage auf Anforderungen (neuer User loggt sich ein o.Ä.) zu reagieren.
Oh .. Python kann Threads? Respekt! :)
Also starte ich aus dem Main einen Thread, der immer prüft, ob von dem Dictionary was in die DB muss?
Kannst du ein kurzes Code Beispiel geben, wie sowas aussehen könnte?
Also nur den Threadaufruf und ob das Dictionary im Thread sichtbar sein muss, oder was da überhaupt sichtbar sein muss.
Dookie hat geschrieben: Hoffe etwas geholfen zu haben :)
Auf jeden Fall!
Jetzt weiß ich wenigstens, dass ich den Server wohl wirklich in Python schreiben werde :)

Aber es wäre schön, wenn du mir die Fragen nochmal beantworten könntest ;)

Bye, Transmitter
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi nochmal,
Transmitter hat geschrieben:Dictionary ist, wie ich in einem Buch lesen kann, eine nicht sortierte Liste von Objekten?
Also vergleichbar mit einer Collection in Java?

In dieser Collection haben dann alle Benutzerobjekte Platz, sie werden erzeugt, und mit ihren Daten in die Liste geschrieben?
Falls ich ein Objekt brauche, wird auf dieses Anhand eines Namens ( key ) zugegriffen?
Ja ein Dictionary könnte man auch als assoziatives Array bezeichnen. Der Schlüssel (key) währ wohl bei deiner Anwendung dann die UserID. Als Schlüssel kann in Python jeder unveränderliche (imutable) Datentyp also ausser Strings auch int oder sogar tuple verwendet werden.
Transmitter hat geschrieben:
Dookie hat geschrieben:solange der Speicher reicht sollte es kein Problem geben.
Kannst du grob abschätzen, wieviel sowas benötigt?
Oder kommt ich mit 1GB Ram für Python, Webserver UND MySQL DB hin?
Dazu müsste ich wissen wieviele Daten pro User gespeichert werden. Mal angenommen es währen pro User 1 KB dann das mal 5000 sind 5 MB plus Sclüssel und Speicher den die Objekte intern noch belegen kannst du 6 MB rechnen. Also kommst Du mit 1 GB sicher aus.
Transmitter hat geschrieben:Oh .. Python kann Threads? Respekt! :smile:
Also starte ich aus dem Main einen Thread, der immer prüft, ob von dem Dictionary was in die DB muss?
Kannst du ein kurzes Code Beispiel geben, wie sowas aussehen könnte?
Also nur den Threadaufruf und ob das Dictionary im Thread sichtbar sein muss, oder was da überhaupt sichtbar sein muss.
Jo Python kann auch Threads, ist ja eine sehr moderne Sprache :)

Ich beschreib erstmal wie ich das ganze anlegen würde und mach später, wenn ich dazu komme noch ein bisschen Beispielcode.


Hauptprogramm wartet auf Anfragen, holen, ändern von Userdaten,.

Wenn eine Anfrage "holen" kommt, wird erstmal geschaut, anhand der UserID ob die Daten schon im Dictionary sind, sonst werden die aus der Datenbank geholt und in das Dictionary eingetragen.
Zu den Daten kommt noch ein Timestamp, der angibt wann der letzte Zugriff auf die Daten erfolgte.
Jetzt können die Daten ans php geschickt werden.

Beim "ändern" werden die Userdaten im Dictionary geändert und als geändert markiert, so brauchen nur auch wirklich geänderte Daten vom zweiten thread in die Datei geschrieben zu werden.


Der zweite Thread (alle 10 Minuten), durchläuft das Dictionary.
Löscht veraltete Daten bzw. schreibt geänderte Daten in die Datenbank und wartet wieder 10 Minuten.

Hier noch der Link zum Threadingmodul:
http://www.python.org/doc/current/lib/m ... ading.html
Zum Warten verwendet man sleep(sec) aus dem Modul time


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Transmitter
User
Beiträge: 24
Registriert: Mittwoch 24. November 2004, 02:23

Dookie hat geschrieben: Dazu müsste ich wissen wieviele Daten pro User gespeichert werden. Mal angenommen es währen pro User 1 KB dann das mal 5000 sind 5 MB plus Sclüssel und Speicher den die Objekte intern noch belegen kannst du 6 MB rechnen. Also kommst Du mit 1 GB sicher aus.
Habe was über Speicherbedarf gefunden.

Klassen-Instanz: 16Byte + 1 Dictionary Objekt ca. 25Byte
Ca. 10 Zahlen pro Objekt: 10 * 12Byte
Ca. 10 Strings pro Objekt: 10 * ca. 200Byte

Also über 100MB Ram sollte das Programm am Anfang nicht haben, das ist großzügig und passend kalkuliert :).

Dookie hat geschrieben: Hauptprogramm wartet auf Anfragen, holen, ändern von Userdaten,.
Das Hauptprogramm wäre dann auch ein Thread, der so lange schläft, bis er aktiviert wird?
Also würdest du 2 Threads vorschlagen, einmal einen für den Server an sich ( Hauptprogramm SELECT, UPDATE, usw. ) und einen Thread der nach 10 Minuten eine Methode aufruft um die aktuellen Daten in die DB zu schreiben?


Wie ist denn das mit dem Dictionary, das muss dann global sichtbar sein, damit beide Thread da drauf dürfen.
Sind die Threads dann automatisch synchronisiert oder wie mache ich da am besten?
Und wie bekomme ich da aktive warten des 2. Threads hin, der sich ja irgendwie selbst nach 10 min aufrufen muss.
Mit 2 Methoden die sich abwechseln:

Code: Alles auswählen

def Schlafen ( self ):
    sleep ( 600 )
    self.Update ( )

def Update ( self ):
    UPDATE DB usw.
    self.schlafen ( )
funktioniert sowas?
Ich habe in einem Buch folgendes gesehen:

Code: Alles auswählen

def print_time(delay):
    while 1:
        time.sleep(delay)
        print time.ctime(time.time())
# Starte den neuen Thread.
thread.start_new_thread(print_time, (5,))
Das ist doch auch noch busy-wating, oder ist es das wegen des slepp Aufrufes nicht mehr?
Dookie hat geschrieben: Hier noch der Link zum Threadingmodul:
http://www.python.org/doc/current/lib/m ... ading.html
Oh .. Threading?
Damit ich die Threads synchronisieren kann?

Danke dir schon mal / noch mal für deine ausführliche und kompetente Hilfe, sowas erfährt man gerne :)
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Transmitter hat geschrieben:Das Hauptprogramm wäre dann auch ein Thread, der so lange schläft, bis er aktiviert wird?
Also würdest du 2 Threads vorschlagen, einmal einen für den Server an sich ( Hauptprogramm SELECT, UPDATE, usw. ) und einen Thread der nach 10 Minuten eine Methode aufruft um die aktuellen Daten in die DB zu schreiben?
Das Haupprogramm ist auch ein Thread, den du aber nicht explizit startest. Wenn Du ein Script startest hast du ja auch eben diesen einen Thread, auch wenn du keine anderen Threads erzeugst.
Im Hauptprogramm wartest du bis ein z.B. TCP/IP-Request kommt und dann reagierst Du darauf, also holen neuer Datensätze und aktualisieren der Timestamps von angeforderten Datensätzen. Wenn du einen der in Python schon integrierten Server verwendest, brauchst du dich um die TCP/IP Sachen nicht kümmern. Das geht dann über die entprechenden Objekte. Siehe http://www.python.org/doc/current/lib/m ... erver.html
Vorher wird der 2. Thread gestartet, der dann alle 10 Minuten die überalteten Datensätze löscht bzw. zu schreibende wieder in die Datenbank schreibt.
Transmitter hat geschrieben:Wie ist denn das mit dem Dictionary, das muss dann global sichtbar sein, damit beide Thread da drauf dürfen.
Sind die Threads dann automatisch synchronisiert oder wie mache ich da am besten?
Und wie bekomme ich da aktive warten des 2. Threads hin, der sich ja irgendwie selbst nach 10 min aufrufen muss.
Mit 2 Methoden die sich abwechseln:
Du kannst das Dictionary auch als Parameter an den 2. Thread mitgeben. Bei Python gilt immer "call by reference" also es wird nicht das ganze Dictionary übergeben sondern nur eine Referenz darauf. Damit nicht gleichzeitig beide Threads auf das Dictionary zugreifen würde ich es mit einer Semaphore absichern. http://www.python.org/doc/current/lib/s ... jects.html
Das lässt sich dann auch in einer eigenen Klasse für ein threadsave Dictionary realisieren:

Code: Alles auswählen

class TSDict(dict):
    def __new__(cls, arg):
        self = super(TSDict, cls).__new__(cls, arg)
        self._semaphore = Threading.Semaphore()

    def aquire(self):
        self._semaphore.aquire()

    def release(self):
        self._semaphore.release()

    def __getitem__(self, key):
        self.aquire()
        result = super(TSDict, self).__getitem__(key)
        self.release()
        return result

    def __setitem__(self, key, value):
        self.aquire()
        super(TSDict, self).__setitem__(key, value)
        self.release()

    def __delitem__(self, key):
        self.aquire()
        super(TSDict, self).__delitem__(key)
        self.release()

    def keys(self):
        self. aquire()
        result = super(TSDict, self).keys()
        self.release()
        return result

    # ... weitere benötigte Methoden

Code: Alles auswählen

def print_time(delay):
    while 1:
        time.sleep(delay)
        print time.ctime(time.time())
# Starte den neuen Thread.
thread.start_new_thread(print_time, (5,))
ist kein busywaiting eben wegen dem time.sleep(), das legt einen Thread für delay-sekunden schlafen und in der zeit können andere Threads ihre arbeiten erledigen.


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Transmitter
User
Beiträge: 24
Registriert: Mittwoch 24. November 2004, 02:23

Ich weiß nicht, ob ich das so richtig verstehe, oder ob mir da einfach vom Design-Aspekt was fehlt.

Ich habe jetzt ein UML Diagramm, was so aussieht:
http://www.haustierworld.de/PythonServer.jpg

XMLRPCServer ist die Klasse, die von twisted abgeleitet ist und die PHP Anfragen entgegen nimmt.
Wird eine Methode BaueIrgendwas aufgerufen, dann ruft diese wiederrum eine Methode von GameServer.BaueIrgendwas ( SpielerID, ... ) auf.
Das sollte ja funktionieren.

Aber:
Mit dem XMLRPCServer wird auch der ZeitIntervall gestartet, der 10 min lang schläft und dann dem GameServer irgendwie bescheid sagen muss, dass er das Update auf die DB fahren soll.
Soll ich da einfach die Instanz des GameServers an das ZeitIntervall Objekt übergeben?

Mein GameServer beinhaltet jetzt das dictionary mit den ganzen Spielerobjekten und ist abgeleitet von Semaphore.
Das ist soweit richtig?
Also habe ich mit der GameServer Instanz einen passiven Server, der Zugriffe von XMLRPCServer UND ZeitIntervall synchronisieren muss?
Ist das auch sinnvoll?
( Also passiv deswegen, weil der Thread und der RPC Server auf den GameServer zugreifen und diese Zugriffe synchronisiert werden müssen.
Der GameServer selbst macht natürlich auch was: Er verwaltet die User, deswegen ist er in dieser hinsicht wieder aktiv. )

Glaube den Rest bekomme ich dann selbst hin :)
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

hmm da schaut (für mich) einiges etwas durcheinander aus ;)

Nach deinem ersten Posting dachte ich, du hast schon den Gameserver und wolltest nur den Zugriff auf die Datensätze der Datenbank mittels RPCServer puffern.
Darum meine erste Frage, warum ist jetzt XMLRPCServer von Twisted abgeleitet?
Gameserver sollte egal was der macht nicht von Semaphore abgeleitet sein, er ist ja keine Semaphore, die den Zugriff auf das Dictionary regelt, welches die Spielerdaten enthält. Also falls der jetzt für das Verwalten des Dictionaries zuständig ist und für das aktualisieren der Daten in der Datenbank so würd ich dem nen anderen Namen geben.
Ich mach mal ein einfaches ascii-diagramm wie ich mir das so vorgestellt hatte.

Code: Alles auswählen

+--------------+
|   Datenbank  |
+--------------+
     ^     |
    / \    |
     |    \ /
     |     v
+--------------+
| Spielerdaten | Datenverwaltungsserver:
| Verwaltung   | erledigt das speichern und löschen der Spieler 
| enthält Dict | alle 10 Minuten in einem eigenen Thread
|und Semaphore |
|startet Thread|
+--------------+
     ^     |
    / \    |
     |    \ /
     |     v
+--------------+
| Spieleserver | Eigentlicher Spielserver holt und gibt Daten an den Puffer
+--------------+
Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Transmitter
User
Beiträge: 24
Registriert: Mittwoch 24. November 2004, 02:23

Dookie hat geschrieben: Nach deinem ersten Posting dachte ich, du hast schon den Gameserver und wolltest nur den Zugriff auf die Datensätze der Datenbank mittels RPCServer puffern.
Oh, nein, dann war das ein Mißverständnis:
Ich habe ein PHP / HTML Frontend, eine Klasse die auf einen XMLRPC Server zugreifen kann und die ( fast ) fertige Datenbank.

Ich habe auch schon ein paar Zeilen Python, aber erst 300 insgesamt oder so, also noch nicht so viele.
Dookie hat geschrieben: Darum meine erste Frage, warum ist jetzt XMLRPCServer von Twisted abgeleitet?
Dieser Server soll die Anfragen von PHP entgegen nehmen, und sie an die entsprechende Server Methoden weiterleiten - so eine Art Wrapper also.

Der ist von twisted abgeleitet, damit er auf die PHP Anfragen reagieren kann:
http://twistedmatrix.com/documents/curr ... lrpc#auto1
Dookie hat geschrieben: Gameserver sollte egal was der macht nicht von Semaphore abgeleitet sein, er ist ja keine Semaphore, die den Zugriff auf das Dictionary regelt, welches die Spielerdaten enthält. Also falls der jetzt für das Verwalten des Dictionaries zuständig ist und für das aktualisieren der Daten in der Datenbank so würd ich dem nen anderen Namen geben.
Dann sollte ich dem Ding wohl nur einen anderen Namen geben, weil genau das soll das Teil machen.
Ich denke mal du siehst als Server etwas an, das das Spielgeschehen leitet, also KI, Kampfsystem, usw.?
Das macht der Server in diesem Falle nicht.
Ich hatte bisher nur geplant diesen Server als Persistenzschicht zwischen Datenbank und PHP anzusehen, und auch nur, um die DB zu entlasten.

Das Kampfsystem und die KI schreibe ich wohl lieber in PHP.

Code: Alles auswählen

+--------------+
|   Datenbank  |
+--------------+
     ^     |
    / \    |
     |    \ /
     |     v
+--------------+
| Spielerdaten | Datenverwaltungsserver:
| Verwaltung   | erledigt das speichern und löschen der Spieler 
| enthält Dict | alle 10 Minuten in einem eigenen Thread
|und Semaphore |
|startet Thread|
+--------------+
     ^     |
    / \    |
     |    \ /
     |     v
+--------------+
| Spieleserver | Eigentlicher Spielserver holt und gibt Daten an den Puffer
+--------------+
Ja, genau .. Persistenzschicht, Puffer, wie auch immer, aber genau das Bild habe ich auch im Kopf bzw. wollte ich mit meinem UML Diagramm modellieren, dann ist wohl nur der Name unglücklich gewählt :-/

Meinst du, dass das dann passt und ich so anfangen kann wie in meinem Klassendiagramm?
Bzw. kannst du in Anbetracht der Tatsache nochmal kurz auf meine Fragen eingehen, da bin ich mir noch ein wenig unsicher :(

Danke dir :)
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Ja, so wirds schon klarer :)

Na ob du das Teil dann von twisted ableitest is dann ja egal.
Also jedenfalls nochmal zu Semaphore, das teil ist nur dazu da, damit nicht gleichzeitig von 2(oder mehr) Threads auf das Dictionary zugegriffen wird. Das Semaphore kann nun der Datenserver enthalten oder das Dictionary (falls du da mit einer eigenen Klasse arbeiten willst). D. h. erstmal semaphore aquren dann auf Dictionary bearbeiten oder Datensatz daraus holen und dann semaphoe wieder releasen. So kann es nicht passieren, daß wenn ein Task auf das Dictionary zugreift, der andere es verändert und du so Datenmüll rausbekommst.

Ansonst kannst loslegen :)


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Transmitter
User
Beiträge: 24
Registriert: Mittwoch 24. November 2004, 02:23

Sehr schön, danke dir :)

Dann werde ich mich jetzt mal an die Arbeit machen :D

Danke dir nochmals für deinen Support, es freut mich echt jemand gefunden zu haben, der einfach mal so kompetente Hilfe anbietet! :)
Antworten