multithread webserver probleme

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

hi,

zuerst der code:

Code: Alles auswählen

# multithreading httpd - test
try:
	import psyco
	psyco.full()
except ImportError:
	print 'Psyco not installed, the program will just run slower'
import SocketServer, socket, BaseHTTPServer, os

class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
	def __del__(self):
		print 'object deleted.'

	def do_GET(self):
		self.send_text('test')

	def send_text(self, text, mimetype='text/html', code=200):
		self.send_response(code)
		self.send_header('Content-type', mimetype)
		self.send_header('Content-Length', str(len(text)))
		self.end_headers()
		self.wfile.write(text)
		self.wfile.flush()

class MyThreadingServer(SocketServer.ThreadingTCPServer):
	def __init__(self, server_address, request_handler, AllowIPs):
		SocketServer.ThreadingTCPServer.__init__(self, server_address, request_handler)
		self.AllowIPs = [mask.split('.') for mask in AllowIPs]

	def server_bind(self):
		SocketServer.ThreadingTCPServer.server_bind(self)
		host, self.server_port = self.socket.getsockname()[:2]
		self.server_name = socket.getfqdn(host)

	def verify_request(self, dummy, client_address):
		def check_ip(mask):
			for mask_part, ip_part in zip(mask, ip):
				if mask_part != ip_part and mask_part != '*':
					return False
			return True
		ip = client_address[0].split('.')
		for mask in self.AllowIPs:
			if check_ip(mask):
				return True
		print "IP [%s] not allowed!" % client_address
		return False

def ServerStart(ListenPort, AllowIPs):
	print "Starte HTTP-Server auf Port .:", ListenPort
	print "Zugelassener IP-Bereich .....:", AllowIPs
	httpd = MyThreadingServer(("", ListenPort), MyRequestHandler, AllowIPs)
	httpd.serve_forever()

if __name__=="__main__":
	ServerStart(ListenPort=6667, AllowIPs=('*.*.*.*'))
das ganze funzt ohne probleme (python 2.5.1 @ windows). wenn ich allerdings mehrmals die seite aufrufe, erhöht sich der verbrauche arbeitsspeicher des prozesses und wird nicht freigeben was dann irgendwann zum crash führt... wüßte jemand wo das problem liegt? ich schätze irgendwie bei dem threadTCPServer mit den threads oder so ähnlich?
BlackJack

Zwei Vermutungen: `psyco` kann eventuell *viel* Speicher benötigen. Den Sinn sehe ich bei einem Webserver, der in aller Regel nicht CPU- sondern E/A-lastig ist, sowieso nicht so ganz.

Das andere ist die `__del__()`-Methode, die bei "Objektzyklen" verhindert, dass der Garbage Collector die Objekte "abräumen" kann.
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

habe beides entfern, hat etwas geholfen. allerdings wächst der speicher immernoch, allerdings langsamer und nur von verschiedenenn usern (ips), womit kann das noch zusammen hängen?
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Vielleicht spawnt er so viele Threads wie er möchte, bevor alte wieder zu verwenden, weil kein Maximal-Limit angegeben ist?
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

das tut er, für jeden GET 1 thread, also eine seite mit 5 bildern = 6 threads... wie kann ich sowas hinbekommen vonwegen alte threads wiederverwenden? (irgendwie erinnere ich mich an den cherrpy wsgi, dort gabs standard 10 worker threads)?
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Da müsstest du einen Pool oder eine Queue bilden. Vielleicht kann dich ja der Quelltext von Paste-HTTP-Server inspirieren, der das Threadverhalten einigermaßen konfigurierbar hält.
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

ah, scheint das richtige zu sein, allerdings nicht gerade leicht für mich pers. das rauszufiltern, damit ich das ganze möglichst minimalistisch habe um mich da reinzusteigern... hast du evtl. noch andere sources?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Lambda hat geschrieben:das ganze funzt ohne probleme (python 2.5.1 @ windows). wenn ich allerdings mehrmals die seite aufrufe, erhöht sich der verbrauche arbeitsspeicher des prozesses und wird nicht freigeben was dann irgendwann zum crash führt...
Hallo Lambda!

Ich habe mich jetzt durch den Code von SimpleHTTPServer, BaseHTTPServer und SocketServer gewühlt.

Wird ein Request vom Browser gesendet, dann wird dieser Request an einen Thread weitergegeben. Dieser Thread arbeitet diesen einzelnen Request ab. Dabei werden zwei Dateiobjekte angelegt, über die mit dem Socket kommuniziert wird.

Die Datei-Objekte werden wieder geschlossen, wenn im StreamRequestHandler die Methode "finish()" aufgerufen wird. Und "finish()" wird von deinem Programm korrekt aufgerufen (ich habe zum Testen "finish()" überschrieben).

Nach dem Abarbeiten beendet sich der Thread von selbst. Es können also hunderte Threads gleichzeitig arbeiten und sollten sich eigentlich nicht in die Quere kommen, da sie unabhängig voneinander arbeiten.

Ich kann keinen Fehler in deinem Code feststellen. -- Bis auf die bereits gerügten "__del__" und "psyco". __del__ kann den GC unterlaufen und psyco bringt dir in deinem Programm nichts, da keine großen Berechnungen zu durchlaufen sind.

Dein Code sollte eigentlich korrekt arbeiten. Und wenn irgendwann mal ein anderes Programm Speicher braucht, dann sollte durch den GC wieder genügend Speicher frei werden.

Jetzt kommt die große Frage:
Stürzt dein Programm wirklich ab??? Oder vermutest du nur, dass es irgendwann mal abstürzen könnte???

Der GC gibt den Speicher nicht sofort frei. Der GC muss Zeit dafür finden und den Bedarf erkennen (das glaube ich irgendwo gelesen zu haben). Wenn genügend Speicher frei ist, dann braucht das Freigeben des Speichers mehr Zeit, als den Speicher bis zu einem gewissen Grad befüllt stehen zu lassen, auch wenn der Speicher nicht mehr vom Programm benötigt wird. Das ist der Grund, weshalb man glauben könnte, dass der Speicher nicht mehr frei gegeben wird.

mfg
Gerold
:-)

PS: Ich habe mir nur den Code von Python 2.4.4 angesehen. Ich gehe aber davon aus, dass in Python 2.5.1 kein Fehler dazu gekommen ist, der evt. ein Objekt im Speicher sperrt.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

danke gerold :) ich bin davon ausgegangen das irgendwann das programm crasht, da ich bisher dachte es wird echt nichtmehr freigegeben. kann es sein das es etwas unnütze ist für jeden request 1 thread zu öffnen, wäre das mit worker threads schneller und würde der GC vielleicht garnicht erst unnütze speicher extra reservieren da die threads dann immer aktiv sind?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Lambda hat geschrieben:kann es sein das es etwas unnütze ist für jeden request 1 thread zu öffnen, wäre das mit worker threads schneller und würde der GC vielleicht garnicht erst unnütze speicher extra reservieren da die threads dann immer aktiv sind?
Hallo Lambda!

Wenn du keine Threads verwendest, dann werden die Anfragen hintereinander abgearbeitet. Es wird aber für jede Anfrage ein RequestHandler erstellt und mit der Arbeit betraut.

Wenn du Threads verwendest, dann werden mehrere RequestHandler gleichzeitig mit verschiedensten Anfragen betraut. Es ändert sich also, dass die RequestHandler nebeneinander arbeiten. Bei wenigen Requests oder wenn sowiso immer alles hintereinander abgearbeitet werden soll, dann sind Threads sinnlos oder sogar gefährlich. Aber auch schon eine Website wie diese Foren-Website besteht aus so vielen Elementen, die vom Browser bei jedem Seitenaufbau angefordert werden, dass sich hier ein Threading-Webserver rentiert. Jedes Bild muss z.B. vom Webserver einzeln abgeholt werden -- abhängig von der HTTP-Version können auch mehrere Anfragen in einem Request zusammengefasst werden, aber darauf ist Python schon vorbereitet.

Wenn du mit Threads arbeitest, dann musst du aber auch darauf achten, Daten zwischen Threads nur über "Thread-sichere" Objekte auszutauschen. Du kannst z.B. nicht jeden Thread in eine Textdatei schreiben lassen, da sich die Threads in die Quere kommen können. Genau so ist es mit Pickle. Sobald du mit Threads arbeitest, solltest du auch einen "Thread-sicheren" Datenunterbau verwenden. Z.B. eine Datenbank wie PostgreSQL, mit der du mit mehreren Verbindungen gleichzeitig arbeiten kannst.

Wenn du dir den Aufwand sparen möchtest, dann verzichte auf Threads. Die rentieren sich sowiso nur bei vielen gleichzeitigen Requests.

Edit:
Du kannst es ja mal testen. Das hier ist ein einfacher XMLRPC-Server: http://www.python-forum.de/topic-5478.html
Greife doch mal auf diesen XMLRPC-Server mit dem Client aus diesem Beitrag zu: http://www.python-forum.de/topic-7545.html
Verändere die Anzahl der gleichzeitigen Anforderungen um raus zu finden, ab wie vielen gleichzeitigen Anforderungen der einfache XMLRPC-Server nicht mehr nach kommt.

Probiere dann den "multithreaded" XMLRPC-Server aus. Du wirst sehen, dass dieser viel mehr gleichzeitige Anfragen schaffst. Du wirst aber auch heraus finden, dass der einfache XMLRPC-Server alles schön hintereinander abarbeitet und dabei auch nicht gerade langsam ist.

Es hängt also wirklich davon ab, was du mit deinem Server erreichen möchtest, ob du Threading einsetzen solltest oder nicht.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

Wenn du Threads verwendest, dann werden mehrere RequestHandler gleichzeitig mit verschiedensten Anfragen betraut. Es ändert sich also, dass die RequestHandler nebeneinander arbeiten. Bei wenigen Requests oder wenn sowiso immer alles hintereinander abgearbeitet werden soll, dann sind Threads sinnlos oder sogar gefährlich. Aber auch schon eine Website wie diese Foren-Website besteht aus so vielen Elementen, die vom Browser bei jedem Seitenaufbau angefordert werden, dass sich hier ein Threading-Webserver rentiert. Jedes Bild muss z.B. vom Webserver einzeln abgeholt werden -- abhängig von der HTTP-Version können auch mehrere Anfragen in einem Request zusammengefasst werden, aber darauf ist Python schon vorbereitet.
das mit dem zusammenfassen habe ich gestern schon probiert, allerdings bekomme ich schon beim 2 element ein "connection software error". wenn jemand ne .html aufruft, wärs schon praktisch das gleich alle benötigten <img>'s mitübertragen werden. wie sähe eine zusammen fassung von request aus? (aus python programmier sicht)
Wenn du mit Threads arbeitest, dann musst du aber auch darauf achten, Daten zwischen Threads nur über "Thread-sichere" Objekte auszutauschen. Du kannst z.B. nicht jeden Thread in eine Textdatei schreiben lassen, da sich die Threads in die Quere kommen können. Genau so ist es mit Pickle. Sobald du mit Threads arbeitest, solltest du auch einen "Thread-sicheren" Datenunterbau verwenden. Z.B. eine Datenbank wie PostgreSQL, mit der du mit mehreren Verbindungen gleichzeitig arbeiten kannst.
:) dann werde ich auf jedenfall postgresql benutzen (hast du evtl. eine empfehlung welche "lib" ich nutzen sollte?), habe auch schon aus dem forum erfahren das PostreSQL query zusammenfassen kann oder ähnliches, was bei großen abfragen wesentlich schneller sein soll als mysql gegebenüber?

nochmals danke für die hilfe :)

MfG
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Lambda hat geschrieben:wenn jemand ne .html aufruft, wärs schon praktisch das gleich alle benötigten <img>'s mitübertragen werden. wie sähe eine zusammen fassung von request aus? (aus python programmier sicht)
Hallo Lambda!

HTTP/1.0 baut für jede Anfrage eine TCP/IP-Verbindung auf.
HTTP/1.1 kann mehrere Anfragen über eine TCP/IP-Verbindung senden.

Das ist also eine Sache, die zwischen dem Browser und dem Server ausgemacht wird. Wie ich gestern gesehen habe, scheint es so, dass Python mit so etwas klar kommt.

Dieser Code in "BaseHTTPServer.py" lässt zumindest darauf schließen:

Code: Alles auswählen

    def handle_one_request(self):
        """Handle a single HTTP request.

        You normally don't need to override this method; see the class
        __doc__ string for information on how to handle specific HTTP
        commands such as GET and POST.

        """
        self.raw_requestline = self.rfile.readline()
        if not self.raw_requestline:
            self.close_connection = 1
            return
        if not self.parse_request(): # An error code has been sent, just exit
            return
        mname = 'do_' + self.command
        if not hasattr(self, mname):
            self.send_error(501, "Unsupported method (%r)" % self.command)
            return
        method = getattr(self, mname)
        method()

    def handle(self):
        """Handle multiple requests if necessary."""
        self.close_connection = 1

        self.handle_one_request()
        while not self.close_connection:
            self.handle_one_request()
Edit:
Siehe: http://de.wikipedia.org/wiki/Http#Protokollversionen

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab interessiert mitgelesen ;)

In der Doku habe ich das gefunden:
protocol_version
This specifies the HTTP protocol version used in responses. If set to 'HTTP/1.1', the server will permit HTTP persistent connections; however, your server must then include an accurate Content-Length header (using send_header()) in all of its responses to clients. For backwards compatibility, the setting defaults to 'HTTP/1.0'.
siehe http://docs.python.org/lib/module-BaseHTTPServer.html

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

For backwards compatibility, the setting defaults to 'HTTP/1.0'.
Hallo Jens!

Hier die Ausgabe des Beispielprogramms von Lambda:

Code: Alles auswählen

J:\Dokumente und Einstellungen\Gerold\Desktop>hallo.py
Starte HTTP-Server auf Port .: 6667
Zugelassener IP-Bereich .....: *.*.*.*
localhost - - [24/May/2007 14:24:02] "GET / HTTP/1.1" 200 -
object deleted.
localhost - - [24/May/2007 14:24:10] "GET / HTTP/1.0" 200 -
object deleted.
^C
J:\Dokumente und Einstellungen\Gerold\Desktop>
Zuerst habe ich den URL mit Firefox aufgerufen.
Dann habe ich den gleichen URL mit "wget" aufgerufen.
Wie man sieht, macht sich der Firefox HTTP/1.1 als Protokoll aus, da er es kann. "wget" hingegen scheint es nicht zu unterstützen und der Server fällt auf HTTP/1.0 zurück. Man muss also nichts verstellen, damit der Server HTTP/1.1-Verbindungen annimmt.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Damit teilt der Client mit welche Protokollversion er kann, nicht welche dann, insbesondere auch vom Server, "gesprochen" wird.
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

Code: Alles auswählen

import SocketServer, socket, BaseHTTPServer, os

def cacheFile(datei):
	return open(datei, 'rb').read()

_bild = cacheFile('%s/htdocs/wow.jpg' % os.getcwd())
_html = '''<html>
	<head>
		<link rel="stylesheet" href="style/style.css" type="text/css" />
	</head>

	<body>
		<img src="/wow.jpg" alt="Europa-Release" title="Europa-Release" align="absmiddle" />
		<p>Hello World!</p>
	</body>
</html>'''
_css = 'body {font-family:arial,sans-serif; background-color: #000000; color: #FFFFFF}'

class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
	protocol_version = 'HTTP/1.1'
	
	def do_GET(self):
		print 'neuer thread'
		if self.path == '/dev':
			self.send_stream(_html, 'text/html', 200, 'Keep-Alive')
			self.send_stream(_css, 'text/css', 200, 'Keep-Alive')
			self.send_stream(_bild, 'image/jpeg', 200, 'close')
		elif self.path == '/style/style.css':
			#self.send_stream(_css, 'text/css', 200, 'close')
			#print 'style.css'
			pass
		elif self.path == '/wow.jpg':
			#self.send_stream(_bild, 'image/jpeg', 200, 'close')
			#print 'wow.jpg'
			pass
		else:
			self.send_stream('Error 404 - File not found!', 'text/html', 404)

	#def handle(self):
	#	print 'handle do it'
	#	print self.protocol_version
	#	self.send_stream(_html, 'text/html', 200)

	def send_stream(self, stream, mimetype, code, connection):
		self.send_response(code)
		self.send_header('Content-type', mimetype)
		self.send_header('Content-Length', str(len(stream)))
		self.send_header('Connection', connection)
		self.end_headers()
		self.wfile.write(stream)
		self.wfile.flush()

	def send_text(self, text, mimetype='text/html', code=200):
		self.send_response(code)
		self.send_header('Content-type', mimetype)
		self.send_header('Content-Length', str(len(text)))
		self.end_headers()
		self.wfile.write(text)
		self.wfile.flush()

class MyThreadingServer(SocketServer.ThreadingTCPServer):
	def __init__(self, server_address, request_handler, AllowIPs):
		SocketServer.ThreadingTCPServer.__init__(self, server_address, request_handler)
		self.AllowIPs = [mask.split('.') for mask in AllowIPs]

	def server_bind(self):
		SocketServer.ThreadingTCPServer.server_bind(self)
		host, self.server_port = self.socket.getsockname()[:2]
		self.server_name = socket.getfqdn(host)

	def verify_request(self, dummy, client_address):
		def check_ip(mask):
			for mask_part, ip_part in zip(mask, ip):
				if mask_part != ip_part and mask_part != '*':
					return False
			return True
		ip = client_address[0].split('.')
		for mask in self.AllowIPs:
			if check_ip(mask):
				return True
		print "IP [%s] not allowed!" % client_address
		return False

def ServerStart(ListenPort, AllowIPs):
	print "Starte HTTP-Server auf Port .:", ListenPort
	print "Zugelassener IP-Bereich .....:", AllowIPs
	httpd = MyThreadingServer(("", ListenPort), MyRequestHandler, AllowIPs)
	httpd.serve_forever()

if __name__=="__main__":
	ServerStart(ListenPort=6667, AllowIPs=('*.*.*.*'))
der aktuelle code dank euch :) leider funktioniert es nicht wirklich, habe mir bissel im netz bezüglich Connection: keel-alive durchgelesen (http://www.http.header.free.fr/http.html) und das schheint richtig zu sein, nur scheint es dem BaseHTTP handler nicht zu interessieren, und die methode handle() verstehe ich nicht wirklich also wie ich die benutzen soll...
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo Lambda!

Ich weiß noch nicht um was es dir genau geht, aber wenn du einen superschnellen Webserver mit Python programmieren möchtest, dann sollte ich dir sagen: "Das geht nicht." :wink:

Nimm stattdessen den Apachen: http://httpd.apache.org/

Was ist es, was du brauchst und warum willst du einen Webserver programmieren? Ist es wegen der Prüfung der Client-IP? --> http://httpd.apache.org/docs/2.2/de/mod ... _host.html

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

naja mir gings darum was ich ne alternative für php gesucht habe. dann hatte ich mich mit wsgi beschäftigt, was allerdings nicht so einfach ist da es umständlicher ist als mit basehttpserver dahinterzusteigen wie es funktioniert.
gegen den apache hätte ich nichts, bloß ich habs mal nach nem tip mit SCGI versucht, hab ich bisher nicht zum laufen gebracht.
ich dachte mir ein python webserver mit ohne viel schnickschnack wäre ausreichend schnell, vorallem da ich vor habe alle .html dateien und die resourcen wie bilder im arbeitsspeicher zu cachen (videos, flashfilme, java-applets etc. allerdings nicht) und bei meiner webseiten größe von nichtmal 10-15mb macht mir das nix aus.
den webserver wollte ich nun programmieren der einfachheit halber, da ich finde das wsgi fürn anfänger nicht ideal ist, da man nicht wirklich lernt son webserver/gateway funzt und tutorials sind auch mangelware.

bei meinem letzten post wollte ich nun wissen wie das möglich ist mehere request in einem thread zu behandeln, also sprich wenn jemand XXX.html aufruft, das ich automatisch die .jpg etc. mitsende und somit für jedes bild ein thread spare.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Lambda hat geschrieben:gegen den apache hätte ich nichts
[...]
den webserver wollte ich nun programmieren der einfachheit halber, da ich finde das wsgi fürn anfänger nicht ideal ist
[...]
bei meinem letzten post wollte ich nun wissen wie das möglich ist mehere request in einem thread zu behandeln
Hallo Lambda!

Das sind jetzt fast schon zu viele Fragen auf einem Haufen. Ich hoffe ich verzettle mich nicht beim Antworten...

1.) Der Apache ist ein **sehr schneller** Webserver, der mit vielen gleichzeitigen Anfragen klar kommt und mit der Zeit immer besser wurde. Es wurden Vorkehrungen getroffen, um DOS-Attacken besser zu überstehen, er wurde sicherer und schneller. Es wurden Möglichkeiten geschaffen, Daten im Speicher und auf der Festplatte zu cachen. Sogar als Proxy kann er eingesetzt werden. Der Apache ist, in meinen Augen, der Webserver schlechthin. Auch wenn es einfachere Webserver geben sollte, die evt. auch etwas schneller sind, so ist der Apache als Gesamtpaket sehr ausgereift und kaum zu schlagen.

Wenn du also eine Website auf dem Apachen laufen lassen kannst, dann tu es. Du wirst nicht so schnell etwas Besseres finden.

2.) Man programmiert sich keinen Webserver, nur weil man Python als Skriptsprache für Internetanwendungen einsetzen will. Es gibt viele tausende fertige Lösungen. Ich muss nur wissen, was erwartet wird und schon kann ich dich zu einer erprobten Lösung führen.

Um das Ganze mal zu konkretisieren:

Verwende z.B. Zope und Plone, wenn du ein fertiges Content Management System verwenden willst.

Verwende CGI unter dem Apachen oder mit dem Python-SimpleHTTPServer, um in einfache Seiten ein wenig Dynamik zu bringen. Ich finde sowieso, dass es keinen besseren Einstieg in die Webprogrammierung mit Python gibt, als sich erst mal mit CGI zu befassen. Pfeif zuerst mal auf Geschwindigkeit. Schau erst mal, dass du **irgendetwas** zum Laufen bekommst.

- http://www.python.org/doc/current/lib/module-cgi.html
- [wiki]CGI[/wiki]
- [wiki]XAMPP mit Python CGI[/wiki]

Dieser CGI-Server http://www.python-forum.de/post-23648.html#23648 ist schon so programmiert, dass er mit Threads arbeitet. Versuche nicht ihn zu optimieren, bevor du deine Anwendung nicht fertig gestellt hast.

Zum Thema Geschwindigkeit: Der Apache bietet einige Möglichkeiten an, Daten zu cachen. Statischer Content fühlt sich im Memory-Cache ziemlich wohl. Dynamischer Inhalt, kann so eingestellt werden, dass dieser ständig neu erstellt wird, falls das nötig ist, oder sich alle paar Minuten/Stunden/Tage erneuert. Man sollte sich zuerst mal darauf konzentrieren, dass die Anwendung funktioniert und erst dann mit der Geschwindigkeit befassen, wenn man merkt dass etwas zu langsam ist. So spart man sich viele, viele, viele unnötige Programmiertage.

Es gibt da auch noch Karrigell. http://karrigell.sourceforge.net/
Karrigell ist genau so etwas, was du zu programmieren versuchst. Es ist ein Python-Webserver, der wunderbar mit Python klar kommt. Karrigell ist von sich aus schon nicht langsam. Aber Karrigell kann, wie jeder andere Webserver, **hinter** einem Apachen betrieben werden. Der Apache kümmert sich dann um das Caching und entlastet damit Karrigell. Das ist wirklich schnell. Überhaupt dann, wenn man statischen Content direkt vom Apachen ausliefern lässt. Aber auch das ist erst dann notwendig, wenn man merkt, dass die Website zu langsam wird. ;-)

- http://www.python-forum.de/topic-9077.html

3.) Thema Multithreading mit dem BaseHTTPServer:
Schon das einfachste Programm, das statt dem "SocketServer.TCPServer" den "SocketServer.ThreadingTCPServer" verwendet, arbeitet mit Threads. Du musst dich um fast NICHTS mehr kümmern. Wichtig ist nur, dass du nicht schreibend von mehreren Threads aus auf die gleichen Objekte zugreifst, wenn diese nicht speziell "thread-safe" sind. Wie ich schon erklärte, meine ich damit schreibenden Zugriff auf globale Variablen genau so wie den schreibenden Zugriff auf File-Objekte, usw.

Es ist komplett ungefährlich, wenn du von mehreren Threads aus auf die gleichen Daten "lesend" zugreifst.

Es ist GUT, wenn die einzelnen Requests in einzelnen Threads abgearbeitet werden. Es gibt keinen Grund, weshalb du dich da einmischen solltest. Jeder Job in einem eigenen Thread -- und gut. Versuche nicht erst irgendwelche Jobs wieder in einen Thread zusammenzulegen. Damit drehst du dich nur im Kreis.

Wenn zehn Leute gleichzeitig deine Website ansehen, dann werden nicht alle zehn Leute gleichzeitig auf einen Link klicken. Du kannst also davon ausgehen, dass wenn zehn Leute deine Website ansehen, dass (geschätzt) im Durchschnitt nur zwei gleichzeitige Requests abzuarbeiten sind. Das ist lächerlich wenig. Meine Website schauen sich täglich ca. 100 Leute an. Aber auf einen ganzen Tag verteilt ist das *nichts*. Ich habe es mit 500 bis 3000 Hits am Tag zu tun. Was sind schon 3000 Requests. Bei einem 10-Stunden Tag wären das 5 Requests pro Minute.

Apache: Schnell bei wenigen gleichzeitigen Zugriffen und schnell bei vielen gleichzeitigen Zugriffen.
CGI: Schnell genug bei wenigen gleichzeitigen Zugriffen. Wird langsamer bei mehreren gleichzeitigen Zugriffen.
Karrigell: Schnell bei wenigen gleichzeitigen Zugriffen. Wird langsamer bei mehreren gleichzeitigen Zugriffen.

Erst dann, wenn die Website zu langsam wird -- und nur dann -- solltest du dir Gedanken über die Beschleunigung machen. In den allermeisten Fällen genügt ein wenig Caching.

Edit: Fast hätte ich es vergessen:
HTTP/1.1 sieht als Standard vor, dass der Client eine Verbindung aufbaut und diese so lange offen hält, bis der Webserver im Header "Connection" "close" zurück gibt. Es braucht, meines Wissens, keine zusätzliche Aktion dafür.

Wenn der Server "Connection" "close" nicht zurück gibt, dann wird die Verbindung sowiso vom Server getrennt. Es ist also nicht unbedingt notwendig, sich um "Connection" "close" zu kümmern, aber es ist auch nicht schlecht.

Der Client ist dafür zuständig, die Verbindung aufrecht zu erhalten und die Requests über diese Verbindung zu übergeben. Der Server muss sich nicht besonders darum kümmern. Wenn der Client HTTP/1.1 kann, dann wird er es auch damit versuchen und erst dann auf HTTP/1.0 zurück schalten, wenn der Server nicht korrekt auf die HTTP/1.1-Anfrage reagiert.

Was ich damit sagen will? Dein Server kümmert sich eh schon automatisch um alles. Du musst nichts mehr dazuprogrammieren. Zumindest habe ich es so verstanden und man möge mich ausbessern, falls ich falsch liege.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

gerold hat geschrieben:1.) Der Apache ist ein **sehr schneller** Webserver, der mit vielen gleichzeitigen Anfragen klar kommt und mit der Zeit immer besser wurde. Es wurden Vorkehrungen getroffen, um DOS-Attacken besser zu überstehen, er wurde sicherer und schneller. Es wurden Möglichkeiten geschaffen, Daten im Speicher und auf der Festplatte zu cachen. Sogar als Proxy kann er eingesetzt werden. Der Apache ist, in meinen Augen, der Webserver schlechthin. Auch wenn es einfachere Webserver geben sollte, die evt. auch etwas schneller sind, so ist der Apache als Gesamtpaket sehr ausgereift und kaum zu schlagen.
Apache ist laut diversen Benchmarks einer der langsameren Server [1][2][3], wobei er insbesondere bei statischen Dateien oft hinterher ist (habe irgnedwo einen Vergleich mit gatling gesehen). Hängt aber auch von seiner Konfiguration ab. In aller Regel ist er aber ausreichend schnell. Apaches Vorteil ist in meinen Augen die hohe Akzeptanz und die vielen Features. Wobei ich zugeben muss, dass Lighttpds Unterstützung für FastCGI und SCGI direkt vom Upstream kommt und daher besser maintained wird. Zu testen wäre auch nginx, aber damit habe ich im Moment keinerlei Erfahrungen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten