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

Ich hab mal eine allgeimeine Frage, wie ich den Server etwas im hintergrund "berechnen" lasse ohne den User etwas davon "spüren" zu lassen?! geht das überhaupt?

konkretes Beispiel:

- User läd eine Datei hoch mit 100.000.000 Mathe Aufgaben (Multiplikation, ich weiß...dauert nicht lange aber gehen wir davon aus, dass der Server 1 Minute benötigen würde, um das Ergebnis zu liefern)
- es erscheint eine Meldung datei erfolgreich hochgeladen...
- User benutzt die Internetseite weiter (durchstöbern, evtl. nächste datei hochladen)
- User geht 5 Minuten später wieder an seinen Account und zack steht das Ergebnis als Nachricht für ihn bereit

ginge so etwas? sprich der User führt seine Aktivitäten weiter aus und im hintergrund wird etwas ausgeführt/berechnet? Wie würde man so etwas realisieren? also dem Server mitteilen mache etwas gleichzeitig oder im Hintegrund?

Danke

liebe Grüße

Dan
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Würde gehen, wenn du die zwei Aufgaben auseinander koppelst: Also Webseite und das eigentliche "berechnen"...

Brauchst halt einen im Hintergrund laufenden Deamon der unabhängig von dem Webserver die neuen Aufgaben abarbeitet.
Dem Rechner-Job könnte man dann noch die entsprechende CPU-Priorität zuweisen, damit der den Webserver nicht ausbremst...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

ahja und was für ein "daemon" wäre das? (google spuckt daemon-tools aus und das ist nicht ganz das was ich brauche ;) ) tut mir leid wenn ich so dumm frage aber ich kenn mich damit überhaupt nicht aus...

was genau meinst du mit auseinander koppeln ?

Die Daten zum Webserver hochladen und dann dem "daemon" sagen "rechne!" ?

ein paar links oder tuts oder so wären hilfreich :D

Danke
BlackJack

@DanJJo: Das wäre ein Daemon den *Du* schreiben müsstest. Eben ein dauerhaft laufender Prozess der für das berechnen zuständig ist. *Diese* daemontools könnten da tatsächlich nützlich sein, wobei ich supervisord bevorzuge.

Mit entkoppeln ist gemeint, dass der Rechnen-Prozess unabhängig von Deiner Webanwendug läuft. Hier kommt es übrigens wieder stark auf den Webspace-Anbieter an, ob der lang laufende Prozesse erlaubt. Viele billige Anbieter erlauben das nicht und killen Benutzerprozesse nach einer gewissen Laufzeit einfach.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

also sowas in der Art beim Website aufruf starten?! natürlich gestartet bei fileupload und schlafengelegt bis fileupload

Code: Alles auswählen

import daemon
import time

def do_something():
    while True:
        with open("/tmp/current_time.txt", "w") as f:
            f.write("The time is now " + time.ctime())
        time.sleep(5)

def run():
    with daemon.DaemonContext():
        do_something()
EDIT*: Könnte ich denn supervisord auf einem Server installieren und dort einfach starten ?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Auf einem Shared Webspace kann man länger laufende Prozesse und starkte CPU Last i.d.R. eh vergessen...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@DanJJo: Nicht beim Webseitenaufruf starten sondern generell parallel zum Webserver ausführen. Halt einen Server der Berechnungen durchführt.

Natürlich kannst Du ``supervisord`` auf einem Server installieren. Natürlich nur wenn Du das darfst, also die nötigen Rechte dazu hast und wenn Du langlaufende Prozesse haben kannst. Falls es zufällig um Uberspace als Hoster geht, die haben Unterstützung für ``daemontools``: Uberspace.de — daemontools.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

Also ich hab mich ein wenig mit diesen daeomonen befasst und ich weiß nicht, ob ich es richtig mache...

Ich hab daemontools installiert und ind die /etc/init/svscan.conf folgendes geschrieben.

Code: Alles auswählen

     start on runlevel [12345]
     stop on runlevel [^12345]
     respawn
     exec /command/svscanboot
weitere verstehe ich nun nicht wie das mit den "Service" Ordner abläuft. Erstelle ich einen neuen mkdir -p /etc/service/testdaemon Ordner und gebe diesem chmod 1755 und lege dort meine test_daemon.py ab ? und starte dann diese mit exec setuidgid testdaeomon /usr/bin/python test_daemon.py ? also bei mir verschwindet dann nur das Terminal und nichts passiert also mach ich bestimmt irgendetwas falsch, da in die current_time.txt nichts geschrieben wird.

Danke :)

sonnigen Sonntag noch!

Grüße

Dan

die test_daemon.py nochmal...

Code: Alles auswählen

import daemon
import time

def do_something():
    while True:
        with open("/tmp/current_time.txt", "w") as f:
            f.write("The time is now " + time.ctime())
        time.sleep(5)

def run():
    with daemon.DaemonContext():
        do_something()
EDIT*:
also ich hab mein "daemon" zum laufen bekommen mit dieser Anleitung http://thedjbway.b0llix.net/daemontools/blabbyd.html. Mit svstat /service/test_daemon sehe ich auch das mein daemon läuft aber er speichert nichts in curren_time.txt...

meine run ruft die python datei mit exec python test_daemon.py auf. Erkennt einer meinen Fehler ?
BlackJack

@DanJJo: Als erstes solltest Du prüfen ob beide „Enden” die Du verbinden möchtest für sich genommen funktionieren. Also auf der einen Seite ob die daemontools laufen, also ob tatsächlich ein `svscan`-Prozess läuft, der ein Service-Verzeichnis überwacht.

Eine ``/etc/init/svscan.conf`` anlegen hat erst einmal keinen Effekt bis man entweder den Rechner neu startet, oder (ich gehe mal von `upstart` aus) mit ``initctl start svscan`` mit den entsprechenden Rechten diesen Dienst startet.

An der Stelle wundere ich mich nämlich über das ``/command/``. Falls das nicht eine besonderheit vom Init-System ist — da kenne ich mich jetzt nicht so gut aus — dann gibt es diesen Pfad nicht und ``svscanboot`` kann so nicht gestartet werden. Ich habe an der Stelle ``exec /usr/bin/svscanboot`` stehen, was auch dem kompletten Pfad zu ``svscanboot`` auf meinem System entspricht.

Am anderen „Ende” hast Du Deinen Testdaemon. Der läuft natürlich nicht, denn es werden nur zwei Funktionen definiert und das war es dann auch schon. Das hätte man ziemlich schnell bemerkt, wenn man den einfach mal von Hand gestartet hätte. Da fehlt also so etwas wie

Code: Alles auswählen

if __name__ == '__main__':
    run()
damit sich überhaupt etwas tut.

Wo Du das Programm ablegst ist nahezu egal. In dem Verzeichnis für den Service muss ein Skript mit dem Namen ``run`` liegen welches das Programm *im Vordergrund* startet.

Das bedeutet das `daemon`-Modul brauchst Du überhaupt nicht, im Gegenteil das darfst Du so gar nicht verwenden weil es den Prozess in den Hintergrund befördert. Aus der daemontools-FAQ:
http://cr.yp.to/daemontools/faq/create.html#run hat geschrieben: How do I create a service directory?

Answer: The only required component of your service directory is an executable file, ./run, that runs your daemon in the foreground, exiting when your daemon exits.
Minimales Beispiel:

Code: Alles auswählen

bj@god:~$ ls /etc/service/testdaemon
run  supervise  testdaemon.py
bj@god:~$ cat /etc/service/testdaemon/run 
#!/bin/bash

exec ./testdaemon.py
bj@god:~$ cat /etc/service/testdaemon/testdaemon.py 
#!/usr/bin/env python
import time


def do_something():
    while True:
        with open('/tmp/current_time.txt', 'w') as time_file:
            time_file.write('The time is now {0}\n'.format(time.ctime()))
        time.sleep(5)


if __name__ == '__main__':
    do_something()
bj@god:~$ sudo svc -u /etc/service/testdaemon/
bj@god:~$ sudo svstat /etc/service/testdaemon
/etc/service/testdaemon: up (pid 16611) 3 seconds
bj@god:~$ sudo svstat /etc/service/testdaemon
/etc/service/testdaemon: up (pid 16611) 6 seconds
bj@god:~$ cat /tmp/current_time.txt 
The time is now Sun Aug  4 12:55:50 2013
Das ``supervise/``-Unterverzeichnis legen die daemontools an, da sind Verwaltungsdaten und Statusinformationen gespeichert.

Solange man einen Dienst nicht mit ``svc -d`` stoppt, und der Dienst nicht schon läuft, versuchen die daemontools regelmässig das ``run``-Skript auszuführen. Wenn man an dem etwas ändern möchte sollte man das nicht direkt tun, sondern an einer Kopie und die dann am Ende in ``run`` umbenennen, damit es nicht passieren kann, dass die daemontools eine ``run`` ausführen an der man gerade arbeitet.
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

:roll: Danke hat mir sehr weiter geholfen - die main war vorhanden nur hab ich sie nicht gepostet - mein fehler. Daemon-tools auf seinem system zum laufen zu bringen funktioniert wie ich gelesen habe auf mehrere Art und Weisen. Meine "läuft" nach der beschreibung im obigen link. Ich dachte ich müsste die test_daemon.py normal aufrufen mit python test_daemon.py und nicht wie eine shell mit ./test_daemon.py ... aber er läuft und ich bin zufrieden :mrgreen: jetzt bastel ich mal ein wenig an der Funktion und schaue mal, ob ich es hin bekomme ihn rechnen zu lassen und mir ein Ergebnis zu liefern :) Danke nochmal...

EDIT*:
also ich hab mal Angefangen und viele Fragen kommen auf.

Sagen wir ich hab eine def aus einer main.tpl die so aussieht:

Code: Alles auswählen

import daemon_folder.test_daemon as daemon
install(SQLitePlugin(dbfile="test.db"))
...
@route("/berechne_lang/", method='POST')
def berechne_lang(db):
	# get data from template
	data = request.forms.getall('datas')
        daemon(data[0],data[1])
        return template("hauptmenu")
und einen Daemon der folgendes macht:

Code: Alles auswählen

#!/usr/bin/env python
install(SQLitePlugin(dbfile="test.db"))

def do_something(x,y):
	  erg = x + y
          row = db.execute('INSERT INTO erg (erg) values (?)',(erg,))


if __name__ == '__main__':
    do_something(x,y)
Angenommen do_something braucht 2 Minuten bis es ein Ergebnis liefert. Wenn ich das so starte wird der Daemon ausgeführt und läuft bis zum Schluss und dann erst wird das Template hauptmenu zurück gegeben...das ist quatsch, da der Daemon ja eigentlich so gedacht ist, dass die Werte an Ihn übergeben werden, im anschluss direkt das hauptmenu template aufgerufen wird und der Daemon irgendwann das Erg. in die db schreibt. Sonst bräuchte ich auch keinen daemon...liegt der fehler darin, dass ich den daemon nicht importen darf? soll ? aber wie rufe ich ihn dann auf? und kann ich jeweils dem main.tpl und der daemon.py die gleiche db geben ?
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

So würde es funktionieren aber es scheint mir nicht wirklich "elegant" und "effizient" zudem wird mein Rechner spürbar langsamer und wärmer :mrgreen:

das ist jetzt noch ein bsp ohne db und templates etc um heraus zu finden "how to make it happen" :K

Ich schreibe was ich möchte in eine file und lasse den Daemon diese Auslesen....

daemon.py

Code: Alles auswählen

#!/usr/bin/env python
import os

def summe():
      while True:
	  if os.path.getsize('/tmp/worker.txt') > 0:
	    with open('/tmp/worker.txt', 'r') as infile:
		values = infile.read().split('#')
		erg = int(values[0])+int(values[1])
		with open('/tmp/rslt.txt', 'w') as wrslt:
			wrslt.write("Erg ist {0}".format(erg))
		open('/tmp/worker.txt', 'a').close()
	    else:
		pass
if __name__ == '__main__':
    summe()
berechne.py

Code: Alles auswählen

def berechne_lang(x,y):
        with open('/tmp/worker.txt', 'w') as outfile:
		outfile.write("{0}#{1}".format(x,y))
	return "fertig_berechne_lang"
es muss doch eine besser lösung geben oder?!
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@DanJJo: Dein Prozess verbrät nicht nur seine eigene Rechenzeit, sondern beschäftigt auch noch den Kernel andauernd.
Wenn Du tatsächlich auf Dateisystembasis arbeiten willst, gibt es inotify oder ähnliches um Änderungen in Verzeichnissen resourcenschonend abzufragen. Alternativ sei auf alle möglichen Arten der Interprocesskommunikation hingewiesen (Pipes, Sockets, ...)
DanJJo
User
Beiträge: 90
Registriert: Mittwoch 13. Februar 2013, 18:35

Sirius3 hat geschrieben:@DanJJo: Dein Prozess verbrät nicht nur seine eigene Rechenzeit, sondern beschäftigt auch noch den Kernel andauernd.
Wenn Du tatsächlich auf Dateisystembasis arbeiten willst, gibt es inotify oder ähnliches um Änderungen in Verzeichnissen resourcenschonend abzufragen. Alternativ sei auf alle möglichen Arten der Interprocesskommunikation hingewiesen (Pipes, Sockets, ...)

die Frage ist: Muss ich überhaupt auf Dateisystembasis arbeiten ?! oder gibt es eine andere Möglichkeit dem Daemon Werte zu übergeben? EDIT* bevor eine Antwort kommt ich probiers mal mit einer Pipe
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Die abzuarbeitenden Daten in eine Datenbank schreiben, die gleichzeitig vom Deamon gelesen werden?

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

jens hat geschrieben:Die abzuarbeitenden Daten in eine Datenbank schreiben, die gleichzeitig vom Deamon gelesen werden?
Dann Pollt man aber die Datenbank, das ist auch nicht viel besser.

Ich frag mich eigentlich warum noch niemand Celery erwähnt hat, das wäre genau das worin man Hintergrundberechnungen aus ner Webapp verlagern kann.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
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:
Antworten