Seite 1 von 1

(Django) SMTP-Verbindung offen halten?

Verfasst: Mittwoch 11. März 2009, 16:34
von metty
Hallo zusammen,

ich programmiere mit Django eine relativ große Webapplikation und es klappt alles soweit wunderbar. Ich bin aber auf ein kleines "Schönheitsproblem" gestoßen.

Ich versende über "EmailMultiAlternatives" (django.core.mail) html- bzw. text-eMails, das funktioniert auch wunderbar.

Das Problem ist jedoch, dass der eMail-Server nicht lokal läuft und so erst eine Verbindung zum Mailserver aufgebaut werden muss und das dauert immer etwas. Für den User hat das den Anschein, als ob der Befehl die eMail zu senden nicht angekommen ist, da die Programm-Abarbeitung hier "steht", bis die email korrekt versendet wurde. So kommt der Benutzer vielleicht auf die Idee nochmal zu klicken. Bei "EmailMultiAlternatives" kann jedoch ein Connection-Objekt angegeben werden. Sprich eine bestehende SMTP-Verbindung.

Wo bzw. wie kann eine solche SMTP-Verbindung schon vor dem versenden öffnen (z.B. beim Aufruf des Django-apps), dass das Versenden der Mail schneller von statten geht?
Kann ich das in sowas wie einem Konstruktor des jeweiligen App's angeben?


Vielen Dank.

Verfasst: Mittwoch 11. März 2009, 18:26
von sma
Da für jeden Request ja Django ja quasi neu gestartet wird, funktioniert es prinzipiell nicht, dass etwas permanent läuft oder verbunden bleibt.

Dein Problem lässt sich tpyischerweise durch einen Queue-Service lösen, in den der Auftrag eine Mail zu verschicken eingestellt wird (was in der Regel sehr schnell geht) und dann später abgearbeitet wird, wenn Django schon wieder schläft und auf den nächsten Request wartet.

Im Prinzip kann man sich so was auch selbst bauen, in dem du in einem Tabelle einen passenden Eintrag schreibst und dann in einem zweiten Prozess (oder mehreren) regelmäßig in der Datenbank nachschaust, ob etwas zu tun ist und das dann tust.

Da dieser zweite Prozess vielleicht auch den ORM von Django benutzen will, ist es am einfachsten, ihn als custom commands zu implementieren, da diese aus der richtigen Umgebung direkt auf die Modelle zugreifen dürfen.

Statt einer Datenbank bietet sich auch häufig ein gemeinsamer Cache (z.B. memcached) an, da dies wesentlich effizienter ist. Und nicht jeder braucht ACID auf Queues, wie es z.B. JMS im Java-Umfeld bieten würde.

Schau dir mal http://code.google.com/p/django-queue-service/ an. Andere Stichworte sind Starling oder ActiveMQ, die haben aber nichts direkt mit Django zu tun. Vielleicht taugt http://code.google.com/p/peafowl/ etwas..?

Stefan

Verfasst: Mittwoch 11. März 2009, 19:01
von metty
sma hat geschrieben:Da für jeden Request ja Django ja quasi neu gestartet wird, funktioniert es prinzipiell nicht, dass etwas permanent läuft oder verbunden bleibt.

Dein Problem lässt sich tpyischerweise durch einen Queue-Service lösen, in den der Auftrag eine Mail zu verschicken eingestellt wird (was in der Regel sehr schnell geht) und dann später abgearbeitet wird, wenn Django schon wieder schläft und auf den nächsten Request wartet.

Im Prinzip kann man sich so was auch selbst bauen, in dem du in einem Tabelle einen passenden Eintrag schreibst und dann in einem zweiten Prozess (oder mehreren) regelmäßig in der Datenbank nachschaust, ob etwas zu tun ist und das dann tust.

Da dieser zweite Prozess vielleicht auch den ORM von Django benutzen will, ist es am einfachsten, ihn als custom commands zu implementieren, da diese aus der richtigen Umgebung direkt auf die Modelle zugreifen dürfen.

Statt einer Datenbank bietet sich auch häufig ein gemeinsamer Cache (z.B. memcached) an, da dies wesentlich effizienter ist. Und nicht jeder braucht ACID auf Queues, wie es z.B. JMS im Java-Umfeld bieten würde.

Schau dir mal http://code.google.com/p/django-queue-service/ an. Andere Stichworte sind Starling oder ActiveMQ, die haben aber nichts direkt mit Django zu tun. Vielleicht taugt http://code.google.com/p/peafowl/ etwas..?

Stefan
Die Idee mit dem Datenbankeintrag hatte ich auch schon, habe sie aber wieder verworfen, da ich sie nicht wirklich effizient gefunden habe. Aber ich gucke mir mal deine anderen Vorschläge an.

Eine andere Alternative wäre ein lokaler SMTP-(Queue)-Server, der nur von localhost Mails entgegennimmt in eine Warteschlange stellt und nacheinander an einen Smarthost verschickt. Das kann/macht eigentlich jeder Mailserver...

Werde vermtl. nicht um einen solchen, lokalen "Mini-Mailserver" herumkommen.

Vielen Dank dafür.

Verfasst: Mittwoch 11. März 2009, 22:50
von apollo13
sma hat geschrieben:Da für jeden Request ja Django ja quasi neu gestartet wird, funktioniert es prinzipiell nicht, dass etwas permanent läuft oder verbunden bleibt.
Hu, wie meinst du das? Es sollte ohne Probleme möglich sein eine Connection offen zu halten, kann mich nicht erinnern, dass da irgendwas neu gestartet wird...

Verfasst: Donnerstag 12. März 2009, 10:09
von Leonidas
metty hat geschrieben:Eine andere Alternative wäre ein lokaler SMTP-(Queue)-Server, der nur von localhost Mails entgegennimmt in eine Warteschlange stellt und nacheinander an einen Smarthost verschickt. Das kann/macht eigentlich jeder Mailserver...
Ich finde auch dass ein Relay-Only SMTP-Server wie ESMTP oder MSMTP die für ihren Aufwand beste und simpelste Lösung ist.

Verfasst: Donnerstag 12. März 2009, 11:49
von metty
Leonidas hat geschrieben:
metty hat geschrieben:Eine andere Alternative wäre ein lokaler SMTP-(Queue)-Server, der nur von localhost Mails entgegennimmt in eine Warteschlange stellt und nacheinander an einen Smarthost verschickt. Das kann/macht eigentlich jeder Mailserver...
Ich finde auch dass ein Relay-Only SMTP-Server wie ESMTP oder MSMTP die für ihren Aufwand beste und simpelste Lösung ist.
So kleine SMTP-Server sind genau das richtige für mich, denn ich wollte die Installation so schlank (und sicher) halten wie möglich...

ESMTP scheint hier genau das zu sein, was ich will 8)
Ansonsten werde ich mal freshmeat.net konsultieren

Verfasst: Donnerstag 12. März 2009, 11:51
von metty
apollo13 hat geschrieben:
sma hat geschrieben:Da für jeden Request ja Django ja quasi neu gestartet wird, funktioniert es prinzipiell nicht, dass etwas permanent läuft oder verbunden bleibt.
Hu, wie meinst du das? Es sollte ohne Probleme möglich sein eine Connection offen zu halten, kann mich nicht erinnern, dass da irgendwas neu gestartet wird...

Naja, django ist kein Server-Dienst. Ruft man z.B. .../meineseite/ auf, wird die jeweils zugeordnete Funktion "abgearbeitet", an deren Ende kann z.B. ein Template mit Daten versorgt werden, dann jedoch ist die Ausführung zu Ende.

Verfasst: Donnerstag 12. März 2009, 11:55
von Leonidas
metty hat geschrieben:Naja, django ist kein Server-Dienst. Ruft man z.B. .../meineseite/ auf, wird die jeweils zugeordnete Funktion "abgearbeitet", an deren Ende kann z.B. ein Template mit Daten versorgt werden, dann jedoch ist die Ausführung zu Ende.
Jein. Es ist nicht definiert was passiert. Bei CGI-Applikationen ist die Ausführung an dieser Stelle zuende, aber bei FastCGI oder mod_wsgi läuft die Applikation auch nach dem Request noch weiter und kann in dieser Zeit was auch immer machen.

Verfasst: Donnerstag 12. März 2009, 12:16
von metty
Leonidas hat geschrieben:
metty hat geschrieben:Naja, django ist kein Server-Dienst. Ruft man z.B. .../meineseite/ auf, wird die jeweils zugeordnete Funktion "abgearbeitet", an deren Ende kann z.B. ein Template mit Daten versorgt werden, dann jedoch ist die Ausführung zu Ende.
Jein. Es ist nicht definiert was passiert. Bei CGI-Applikationen ist die Ausführung an dieser Stelle zuende, aber bei FastCGI oder mod_wsgi läuft die Applikation auch nach dem Request noch weiter und kann in dieser Zeit was auch immer machen.
Stimmt, das hatte ich vergessen...
Jedoch habe ich bis jetzt von keiner Möglichkeit gehört eine Art "Dienst", wie eben eine SMTP-Verbindung offen zu halten, laufen zu lassen.

Aber wie gesagt, es gibt ein paar Relay-only SMTP Server bzw. sog. store and forward SMTP Server, die sollten hier genau das richtige sein.

http://emailrelay.sourceforge.net/ ist hier besonders einfach zu konfigurieren.
Einfach "emailrelay --as-server --poll 3600 --forward-to smarthost:smtp" und schon werden gespoolte Mails jede Stunde an einen Smarthost ausgeliefert.

Verfasst: Samstag 14. März 2009, 09:20
von sma
Leonidas hat geschrieben:
metty hat geschrieben:Naja, django ist kein Server-Dienst. Ruft man z.B. .../meineseite/ auf, wird die jeweils zugeordnete Funktion "abgearbeitet", an deren Ende kann z.B. ein Template mit Daten versorgt werden, dann jedoch ist die Ausführung zu Ende.
Jein. Es ist nicht definiert was passiert. Bei CGI-Applikationen ist die Ausführung an dieser Stelle zuende, aber bei FastCGI oder mod_wsgi läuft die Applikation auch nach dem Request noch weiter und kann in dieser Zeit was auch immer machen.
Das ist aber ein Implementierungsdetail und das Konzept ist und bleibt, dass die Anwendung für einen Aufruf lebt und keinen Zustand bis zum nächsten Aufruf überlebt.

Stefan

Verfasst: Samstag 14. März 2009, 10:00
von apollo13
Ist es imho nicht, im Prinzip kannst du dich bei Django schon darauf verlassen, dass es den request überlebt, weil cgi einfach zu lahm ist, wenn du mal nen haufen libs lädst...

Verfasst: Samstag 14. März 2009, 10:24
von sma
IMHO nicht, aber so kommen wir nicht weiter ;) Ich kann keinen Zustand in den nächsten Request rüberretten, denn möglicherweise haben ich 10 voneinander komplett unabhängige Betriebssystem-Prozesse, die beliebig sterben und neu geboren werden und wo auch nicht klar ist, welches Prozess als nächstes dran kommt. Ein geteilter globaler Zustand existiert einfach nicht. Die Anfrage ist unwiderruflich beendet, da ich sie nicht mehr von irgendwo referenzieren kann. Die Nicht-Erreichbarkeit von einem globalen Zustand aus ist aber gerade als tot definiert. Damit stirbt jeder Anfrage nach Erfüllung ihrer Lebensaufgabe, eine HTTP-Response zu generieren.

Die üblichen Rahmenwerke für Python oder Ruby funktionieren alle genau wie PHP nach dem "share nothing"-Prinzip. Wer's anders haben will, braucht einen dedizierten Anwendungsserver a la Zope oder JavaEE.

Stefan