Den Server rechnen lassen (allgemeine Frage) zu Bottle

Django, Flask, Bottle, WSGI, CGI…
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

jens hat geschrieben:Die abzuarbeitenden Daten in eine Datenbank schreiben, die gleichzeitig vom Deamon gelesen werden?
nein das passiert nicht. Vorgehen sollte so aussehen App und Daemon benutzen DB...Daemon berechnet etwas und speichert das in die DB. Es soll sich nichts in die Quere kommen. Wäre zumindest Optimal.

ich habs mal mit einer pipe probiert aber auch die lässt den Rechner ordentlich heiss werden was auch nicht so gut ist :\

daemon.py

Code: Alles auswählen

#!/usr/bin/env python
from multiprocessing import Process, Pipe
import sys

def do_something():
	while True:
		try:
			for val in values:
				values = sys.stdin.readline()
			erg = values[0]+values[1]	
			row = db.execute('INSERT INTO erg (erg) values (?)',(erg,))
		except EOFError:
			pass
if __name__ == '__main__':
    do_something()
"app"

Code: Alles auswählen

import subprocess

def berechne_lang(values):
        p = subprocess.Popen(['python', 'daemon.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        for val in values
		p.stdin.write(str(val))
		p.stdin.flush()
	return "egal_was"
BlackJack

@Leonidas: Na weil das wahrscheinlich wieder einen ganzen Haufen Folgefragen nach sich zieht wie man das auf Uberspace installiert/nutzt. ;-)
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

BlackJack hat geschrieben:@Leonidas: Na weil das wahrscheinlich wieder einen ganzen Haufen Folgefragen nach sich zieht wie man das auf Uberspace installiert/nutzt. ;-)

bestimmt nicht :roll: aber wir wollen doch beim Thema bleiben :mrgreen:
BlackJack

@DanJJo: Mit der Pipe musst Du Doch aber den Daemon von dem Prozess aus starten der die Daten bereit stellt. Dann könntest Du das auch gleich in dem selben Prozess berechnen lassen. Wenn Du mit einem separaten Daemon-Prozess kommunizieren willst, dann doch eher über „named pipes”. Da es dort aber zu Problemen kommen kann wenn mehrere Prozesse gleichzeitig Daten loswerden wollen, ist eine Kommunikation über Sockets vielleicht besser geeignet. Da kannst Du einen eigenen Thread für die Berechnungen machen und gegebenenfalls auch einen Thread pro Client, der die Daten entgegen nimmt und in eine Queue zum bearbeiten ablegt.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

BlackJack hat geschrieben:@DanJJo: Mit der Pipe musst Du Doch aber den Daemon von dem Prozess aus starten der die Daten bereit stellt. Dann könntest Du das auch gleich in dem selben Prozess berechnen lassen. Wenn Du mit einem separaten Daemon-Prozess kommunizieren willst, dann doch eher über „named pipes”. Da es dort aber zu Problemen kommen kann wenn mehrere Prozesse gleichzeitig Daten loswerden wollen, ist eine Kommunikation über Sockets vielleicht besser geeignet. Da kannst Du einen eigenen Thread für die Berechnungen machen und gegebenenfalls auch einen Thread pro Client, der die Daten entgegen nimmt und in eine Queue zum bearbeiten ablegt.

Ohje...irgendwie hab ich mir das alles etwas einfacher vorgstellt :| klingt grade nach einer unlösbaren Aufgaben...setzen 6 :oops:
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab kruz mal die Doku zu Celery angesehen. Wenn ich das richtig verstehe, sind die sog. "brokers" dafür zuständig: http://docs.celeryproject.org/en/latest ... index.html

Werden verschiedene Unterstützt und der eigene (?) ist wohl RabbitMQ: http://docs.celeryproject.org/en/latest ... bitmq.html


@DanJJo: Was willst du überhaupt berechnen? Bist du sicher das dazu überhaupt viel Zeit gebraucht wird?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:@Leonidas: Na weil das wahrscheinlich wieder einen ganzen Haufen Folgefragen nach sich zieht wie man das auf Uberspace installiert/nutzt. ;-)
Wobei ich mir nicht sicher bin ob Celery neu erfinden wesentlich einfacher ist. Celery hat so Probleme wie Task/Datenübermittlung mit seinen Brokern schon längst gelöst, ohne über Pollen auskommen zu müssen. Lediglich Uberspace könnte (wie so oft?) Probleme machen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@Leonidas: Naja, einen einfachen XML- oder JSON-RPC-Service zu schreiben ist sicher nicht sooo schwierig. Und zu dem Teil in Klammern zum Uberspace: Das ist shared Webspace und dafür erlauben die eine Menge, was man bei anderen Hostern und auch für den Preis gar nicht erst versuchen kann. Da scheitert es ja häufig schon daran das man überhaupt einen Dienst dauerhaft laufen lassen kann, der mit Daten versorgt werden kann. Also man bekommt Celery dort wahrscheinlich installiert, aber ob man das mit dem Kenntnisstand vom OP hinbekommt ist so die Frage. Wenn das alles automatisiert geht und ohne Probleme durchläuft sicherlich, aber falls es Probleme mit der Umgebung gibt, dann muss man sich unter Umständen ein wenig mit dem Übersetzen von Programmen unter Linux beschäftigen wo die Ergebnisse nicht in systemweite Verzeichnisse installiert werden.

Edit: Wobei ein ``pip-2.7 install Celery`` scheinbar problemlos durchlief. Keine Ahnung ob das jetzt auch tatsächlich benutzbar ist.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

@DanJJo: Was willst du überhaupt berechnen? Bist du sicher das dazu überhaupt viel Zeit gebraucht wird?

jopp läuft momentan ohne daemon etc und dauert um die 2 bis 3 minuten je nachdem...könnte (muss aber nicht) bei weiterer Entwicklung auch länger sein...da die daten nicht sofort relevant sind ist die Rechenzeit in Ordnung und deswegen sollte es auch keine Probleme geben mit dem schreiben in die DB. Optimierbar ist die Rechenzeit auch nicht und . Was Zeit braucht braucht nunmal Zeit ^^ ... den Code zu posten würde hier aber den Rahmen bei weiten sprengen. Werde ich mich wohl mit sockets, threads etc beschäftigen müssen auch wenn es mich davor grault :| - aber keine sorge, Fragen werden kommen, bestimmt :!:

EDIT*: Hab ich das also richtig verstanden? Ich brauche Sockets zwischen Daemon und meiner "App" dann wird für jede Berechnung ein eigener Thread gestartet, sollte es zu mehreren Threads kommen muss ich das mit einer Warteschlange lösen?! Wird doch machbar sein :K :mrgreen:

EDIT2*: TCP oder UDP ? was meint Ihr, da beide Prozesse auf dem selben Server laufen könnte man doch UDP verwenden ?
EDIT3*: Gibts überhaupt IP adressen und Ports für einen Daemon und eine App ? Ich kenn das nur im Zusammenhang mit Client server verbindungen...würde der Daemon sozusagen die Rolle des Server's übernehmen ? aber dann bleibt immer noch die Frage nach der IP und dem Port :K
BlackJack

@DanJJo: Ob Du für jede Berechnung einen eigenen Thread startest bleibt Dir überlassen. Minimal könnte man auch mit zwei Threads auskommen wenn die Übernahme der Daten zwischen App und Daemon schnell von statten geht. Also zum Beispiel wenn da nur wenig Information ausgetauscht wird, wie „in der DB sind neue Daten, hier ist eine ID dafür, rechne mal”. Dann könnte man im Hauptthread auf dem Server-Socket warten und die eingehenden Informationen in eine Queue stecken, die dann von einem Arbeiterthread abgearbeitet wird. Mehrere „gleichzeitige” Anfragen an den Daemon würden vom „backlog” des Server-Sockets abgefangen.

Wenn die Datenübergabe grössere Datenmengen erfordert, dann könnte man im Hauptthread für jede Anfrage einen Thread starten in der die Daten übernommen werden, damit sich mehrere Clients nicht längerfristig blockieren. Und weiterhin einen Arbeiterthread. Oder man rechnet gleich im Thread pro Client. Allerdings wird dann viel Rechenarbeit „parallel” gemacht, aber schneller dürfte das wegen des „global interpreter lock” nicht werden.

TCP oder UDP würde ich nicht davon abhängig machen wo die beiden Partner laufen, sondern welche Eigenschaften man braucht. Wenn es kurze Pakete sind, die auch verloren gehen dürfen UDP, sonst TCP. Also auch bei wenig Information, die aber nicht verloren gehen darf. Denn in der Zeit in der man mit UDP eine Bestätigung und bei Ausbleiben derselben eine Wiederholung bis man die Bestätigung bekommen hat programmiert, wobei man gleichzeitig sicherstellen muss, dass bei einem verzögerten Bestätigen die Informationen nicht doppelt oder mehrfach verarbeitet werden, kann man auch einfach TCP verwenden.

Ähm, App und Deamon *sind* in diesem Falle Client und Server. IP ist 'localhost' beziehungsweise '127.0.0.1', da beide auf dem gleichen Rechner laufen. Und bei der Portnummer musst Du halt rumprobieren bist Du eine Freie irgendwo zwischen 1025 und ich glaube 50000 war's bei Uberspace, gefunden hast, und die zum Beispiel dann vom Daemon in eine Datei schreiben lassen von wo sich die App die dann auslesen kann. Wenn Du auf dem Server alleine wärst, könntest Du einfach eine festlegen, aber bei „shared hosting” besteht ja die Gefahr das jemand anders die Portnummer verwendet.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

BlackJack hat geschrieben:@DanJJo: Ob Du für jede Berechnung einen eigenen Thread startest bleibt Dir überlassen. Minimal könnte man auch mit zwei Threads auskommen wenn die Übernahme der Daten zwischen App und Daemon schnell von statten geht. Also zum Beispiel wenn da nur wenig Information ausgetauscht wird, wie „in der DB sind neue Daten, hier ist eine ID dafür, rechne mal”. Dann könnte man im Hauptthread auf dem Server-Socket warten und die eingehenden Informationen in eine Queue stecken, die dann von einem Arbeiterthread abgearbeitet wird. Mehrere „gleichzeitige” Anfragen an den Daemon würden vom „backlog” des Server-Sockets abgefangen.

Wenn die Datenübergabe grössere Datenmengen erfordert, dann könnte man im Hauptthread für jede Anfrage einen Thread starten in der die Daten übernommen werden, damit sich mehrere Clients nicht längerfristig blockieren. Und weiterhin einen Arbeiterthread. Oder man rechnet gleich im Thread pro Client. Allerdings wird dann viel Rechenarbeit „parallel” gemacht, aber schneller dürfte das wegen des „global interpreter lock” nicht werden.

TCP oder UDP würde ich nicht davon abhängig machen wo die beiden Partner laufen, sondern welche Eigenschaften man braucht. Wenn es kurze Pakete sind, die auch verloren gehen dürfen UDP, sonst TCP. Also auch bei wenig Information, die aber nicht verloren gehen darf. Denn in der Zeit in der man mit UDP eine Bestätigung und bei Ausbleiben derselben eine Wiederholung bis man die Bestätigung bekommen hat programmiert, wobei man gleichzeitig sicherstellen muss, dass bei einem verzögerten Bestätigen die Informationen nicht doppelt oder mehrfach verarbeitet werden, kann man auch einfach TCP verwenden.

Ähm, App und Deamon *sind* in diesem Falle Client und Server. IP ist 'localhost' beziehungsweise '127.0.0.1', da beide auf dem gleichen Rechner laufen. Und bei der Portnummer musst Du halt rumprobieren bist Du eine Freie irgendwo zwischen 1025 und ich glaube 50000 war's bei Uberspace, gefunden hast, und die zum Beispiel dann vom Daemon in eine Datei schreiben lassen von wo sich die App die dann auslesen kann. Wenn Du auf dem Server alleine wärst, könntest Du einfach eine festlegen, aber bei „shared hosting” besteht ja die Gefahr das jemand anders die Portnummer verwendet.
Mutter-Gottes-hilf-mir. Dafür brauch ich doch Jahrzehnte bis ich das umsetze :mrgreen:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

DanJJo hat geschrieben:Mutter-Gottes-hilf-mir. Dafür brauch ich doch Jahrzehnte bis ich das umsetze :mrgreen:
Eben daher die Empfehlung zu Celery. Das kannst du zumindest mal probieren. Vielleicht funktioniert es ja einfach und dann kannst du dir halt das ganze Rad neu Erfinden sparen. Dann kannst du das Redis welches Uberspace bereitstellt als Broker nutzen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

Leonidas hat geschrieben:
DanJJo hat geschrieben:Mutter-Gottes-hilf-mir. Dafür brauch ich doch Jahrzehnte bis ich das umsetze :mrgreen:
Eben daher die Empfehlung zu Celery. Das kannst du zumindest mal probieren. Vielleicht funktioniert es ja einfach und dann kannst du dir halt das ganze Rad neu Erfinden sparen. Dann kannst du das Redis welches Uberspace bereitstellt als Broker nutzen.

Ihr redet immer von den Dingen als wären sie so selbstverständlich wie das Kochen eines Ei's...wenn ich ein so guter Entwickler wäre, würde das bei mir bestimmt mal eben so in ein paar minuten "fluppen" 8) tut es aber leider nicht :mrgreen:

hier mal ein anfang zu der Thread geschichte (bitte beachtet ich hatte noch nie was zu tun damit :) )....celery versuche ich auch mal.

Code: Alles auswählen

import socket
import Queue
import threading
 

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP verbindung erstellen
serversocket.bind('127.0.0.1', "PORT muss ich mir irgendwie suchen durch ausprobieren oO")) # warte auf eingang ueber localhost, wie ich einen freien Port finden soll weiß ich aber auch nicht?! durch try: ?
serversocket.listen(1)

queue = Queue.Queue() # die queue brauche ich für eingehende verbindungen

try:
	while True:
		conn, address = serversocket.accept()
		# packe ich hier schon meine neue conn in die queue ? sprich queue.put(conn,..) unlogisch...mein kopf möchte das nicht da ich doch mehrere threads starte
		thread = threading.Thread(target=do_something, args=[conn]) # als arg übergebe ich meiner funktion die berechnen soll conn damit diese die daten durch sowas wie conn.recv(1024) auslesen kann?
		thread.start()
		# wie meinst du das genau mit dem arbeiter und dem hauptthread. welche sind das ?
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

hab mich doch für den Daemon und die Server/client geschichte entschieden, da der daemon noch möglichkeiten bietet welche mir noch nützlich sein könnten und es nicht falsch ist etwas über sockets, threads und der gleichen zu lernen :wink:

@ BlackJack so hast du das doch gemeint ?! oder mach ich etwas falsch ?

der daemon würde so aussehen...hat einen bekommt alles über den Socket rein und hat einen Arbeitsthread...Was ich noch nicht so richtig verstanden habe ist, wie du das mit dem freien Port bei uberspace meinst?! Wie soll ich den denn finden? durch ausprobieren, also jedes mal neu oder wie hast du dir das gedacht? Weiter..wäre es sinnvoll einen Timer ein zu bauen, der das ganze schlafen schickt solange bis eine anfrage kommt ?

Danke

Code: Alles auswählen

#!/usr/bin/env python
 
import socket
import select
import Queue
from threading import Thread
 
class ProcessThread(Thread):
    def __init__(self):
        super(ProcessThread, self).__init__()
        self.running = True
        self.queue = Queue.Queue()
 
    def add(self, data):
        self.queue.put(data)
 
    def run(self):
        queue = self.queue
        while True:
            try:
                values = queue.get()
                do_something(values)
            except:
                print "blob"
 
thread = ProcessThread()
thread.start()
 
def do_something(values):
		try:
			erg = values[0]+values[1]
			row = db.execute('INSERT INTO erg (erg) values (?)',(erg,))
		except:
			print "blob" 

def main():
    serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serversocket.bind('127.0.0.1', port) 
    serversocket.listen(1)
    while True:
        try:
            conn, addr = serversocket.accept()
            ready = select.select([conn,],[], [],1)
            if ready[0]:
                data = conn.recv(4096)
                thread.add(data)
        except:
	      print "blob"

if __name__ == "__main__":
    main()
Zuletzt geändert von DanJJo am Dienstag 6. August 2013, 12:28, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@DanJJo: einen freien Port bekommst Du automatisch wenn Du Portnummer 0 angibst. Mit »socket.getsockname« erfährst Du auch welchen. Diesen mußt Du dann in eine Datei "daemon.port" schreiben, die vom Web-Server gelesen wird.
»thread« sollte keine globale Variable sein, »select.select« ist überflüssig, bzw. sogar tödlich, da dein Programm nicht vom ersten Client liest, sondern gleich auf den nächsten mit "accept" wartet. »recv« ist, wie schon so oft hier im Forum angemerkt fehleranfällig. Weil Du irgendetwas zwischen 1-4096 bytes liest, was nicht die ganze Nachricht des Clients sein muß. Ein bißchen besseres Protokoll wäre schon angebracht. Warum nutzt Du nicht gleich HTTP, da bringt Python schon den Server- und Clientcode mit.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

also überarbeitet...

ich denke aber, dass meine "Datengröße" konstant sein wird, sodass ich immer die komplette nachricht empfange. Die daemon.port datei liest der Webserver selbst aus oder muss man das auch konfig. ? und was meint ihr zum timer ?

Code: Alles auswählen

#!/usr/bin/env python

import socket
import Queue
from threading import Thread
 
class ProcessThread(Thread):
    def __init__(self):
        super(ProcessThread, self).__init__()
        self.running = True
        self.queue = Queue.Queue()
 
    def add(self, data):
        self.queue.put(data)
 
    def run(self):
        queue = self.queue
        while True:
            try:
                values = queue.get()
                do_something(values)
            except:
                print "blob"

 
def do_something(values):
                try:
                        erg = values[0]+values[1]
                        row = db.execute('INSERT INTO erg (erg) values (?)',(erg,))
                except:
                        print "blob"

def get_full_data(conn):
	full_data=[]
	while True:
		data = conn.recv(8192)
		if not data: break
		full_data.append(data)
	return ''.join(full_data)  

def main():
	serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	serversocket.bind(('127.0.0.1', 0))
	socket_address = serversocket.getsockname()
	with open('daemon.port', 'w') as wp:
		wp.write("{0}".format(socket_address[1]))
	serversocket.listen(1)
	th = ProcessThread()
	th.start()
	while True:
		try:
			conn, addr = serversocket.accept()
			if conn:
				data = get_full_data(conn)
				th.add(data)
		except KeyboardInterrupt:
			break
		except socket.error, msg:
			print "%s" % msg
			break
if __name__ == "__main__":
    main()
Edit*: so jetzt läuft der code auch ^^
Zuletzt geändert von DanJJo am Dienstag 6. August 2013, 14:58, insgesamt 2-mal geändert.
BlackJack

@DanJJo: Wie gross Deine Daten sind, hat damit nichts zu tun. Wenn die Gegenseite sagen wir mal 100 Bytes schickt, dann muss der ``recv(4096)``-Aufruf nicht die ganzen 100 Bytes liefern, sondern kann auch nur 1 Byte liefern. Im Extremfall müsstest Du hundert `recv()`-Aufrufe machen um die kompletten 100 Bytes zu empfangen. Das muss man also in einer Schleife machen bis man sicher ist, dass man die komplette Nachricht erhalten hat.

Natürlich musst Du die Portdatei selbst in Deiner App einlesen. Woher sollte die denn auf magische Weise wissen, dass es da irgendwo eine Datei gibt, wo eine Portnummer drin steht.

Die ``except``\s sind furchtbar. Ich weiss dass das nur Beispiele sind, aber man sollte dort mindestens ein `traceback.print_exc()` ausführen, damit man Informationen über die Ausnahme zu sehen bekommt. Ohne das wird Fehlersuche sehr mühsam.

Beim beenden des Daemons würde ich die Portnummer-Datei wieder löschen.

Es ist durch das blanke ``except`` unnötig schwer das Programm zu beenden. Denn da wird auch ein `KeyboadInterrupt`, also Strg+C unter Linux, abgefangen.

Ein Timer ist nicht nötig weil `socket.accept()` ein blockierender Aufruf ist, genau wie beim `queue.get()`. Wenn da nichts anliegt, sollte der Prozess auch keine Rechenzeit verbrauchen.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

so habs überarbeitet. Ist nu alles drin was drin sein soll ?!

@BlackJack:

Ich dachte der Daemon wird nie beendet ?! wann soll ich denn da die Portnummer-datei löschen?! oder meinst du beim abschluss eines "Rechenzyklus"?!

Ja, dass die App die Portnummer zum verbinden brauch ist klar, woher soll sie sonst wissen an welchen Port tel. muss :roll: war etwas durcheinander weil es hieß "Der Webserver..."
BlackJack

@DanJJo: Grundsätzlich läuft ein Daemon schon „endlos”, aber wenn der Rechner runtergefahren oder neugestartet wird, oder wenn Du den selber mal gerne von Hand stoppen möchtest, zum Beispiel um ihn zu aktualisieren, oder weil mal eine Zeit lang keine Rechnungen durchgeführt werden sollen/müssen, möchte man so etwas eigentlich auch gerne sauber beenden können. Alle anderen Server auf deinem Rechner kannst Du ja auch beenden/herunterfahren. Sollte man jedenfalls können.

An der Stelle müsste man sich auch Gedanken darüber machen, dass das auch mitten beim Rechnen passieren kann, und was dann beim Neustart passiert. Kann man dann eingegangene aber noch nicht berechnete Jobs weiterlaufen lassen, oder sind die dann verloren. (An der Stelle wird sicher gleich wieder jemand Celery erwähnen. :-))
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

noch eine Frage....gibt es eine möglichkeit den daemon zu "beobachten" ? sprich wie ein programm das auf der Console fehler ausgibt etc ?!
Antworten