1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen
  2. Unsere jährliche Weihnachts-Banner-Aktion hat begonnen! Wir freuen uns auf viele, viele kreative Vorschläge.
    Mehr dazu könnt Ihr hier nachlesen: Weihnachtsbanner 2016

    Information ausblenden

PHP/MySQL - Emailverteilersystem

Dieses Thema im Forum "PHP & Co." wurde erstellt von .holger, 23.09.07.

  1. .holger

    .holger Geflammter Kardinal

    Dabei seit:
    13.09.04
    Beiträge:
    9.117
    Moin moin,

    ich habe ein Emailverteiler geschrieben, der so langsam an seine Grenzen stößt. DIe Anzahl der, in einer MySQL Tabelle eingetragen, Adressen ist inzwischen so hoch, dass ich vermutlich beim versenden bald ein Timeout vom Server bekommen werde. Da ich am Server nicht viel verändern kann bzw. auch ohne eine Veränderung der Timeoutzeit möchte, dass das Script funktioniert suche ich nach einer Lösung.

    Der Verteiler läuft wie folgt:

    Ich schreibe die Nachricht, klicke an, welche Gruppen die Mail bekommen soll (Mitglieder, Presse, Interessenten,…), kann in ein Textfeld noch zusätzliche Adressen eintragen und klicke auf absenden.

    Nun sucht sich das Script aus der Tabelle alle Adressen der jeweiligen Gruppenmitglieder und schreibt diese in ein Array, an das Array werden auch die zusätzlichen Adressen geschrieben.
    WEnn alle Adressen in dem Array sind, wird eine Schleife durchlaufen, bei der der E-Mailtext angepasst wird (ein individueller - aus dem Verteileraustragelink wird eingebaut) und dann wird jede Mail einzeln verschickt (mit mail(); ).
    Dies dauerte bisher schon sehr lange (mit rund 144 Adressen) - jetzt habe ich noch rund 300 weitere Adressen eingetragen (der Presseverteiler ist mit dem Mitgliederverteiler zusammengelegt worden).

    Ich würde mich freuen, wenn jemand dafür ein Workarround hätte (vielleicht kann ich nach 50 verschickten mails das Script stoppen, mir die Position im Array merken, die Seite neuladen und von dann das Script erneut starten - mit der alten Position als Ausgangswert) - könnte das klappen? Habt ihr eine bessere Idee?

    Gruß Holger
     
  2. ruelpsnase

    ruelpsnase Tokyo Rose

    Dabei seit:
    07.07.07
    Beiträge:
    71
    Hmm....ohne das Skript zu kennen oder gesehen zu haben, wie Du das gelöst hast, würde ich sagen, dass Du mit Deinem Skript ein grundsätzliches Performanceproblem hast. Ob 150 oder 450 Adressen in einer Tabelle gespeichert sind, dürfte auch bei schwächeren Rechnern kein Problem darstellen.
    Ich habe mich mit MySQL selber schon seit Version 4.x nicht mehr genauer beschäftig, aber ich denke, seit Version 5.x kann man sog. "Stored Procedures" benutzen. Das sind kleine "Miniprogramme", die im MySQL-Server ablaufen - man könnte also die eh schon in der Datenbank vorkommenden Datensätze direkt dort verarbeiten und vielleicht nur die komplett fertige Mail an das PHP-Skript zurückgeben. Vielleicht hilft Dir dieser Hinweis.

    Oder bevor Du die ca. 450 Mails versendest, solltest Du vielleicht nur EINE Mail an dich versenden und die ganzen restlichen Empfänger in das "BCC"-Feld eintragen. Damit bekommt jeder Empfänger die Mail, ohne dass er sieht, wer die Mail noch bekommen hat. Damit würdest Du die Last des Versendens der Mails auf den Mailserver verschieben.

    Es kann auch sein, dass Dein PHP-Skript über dauernde "Repostings" stolpert, d.h. dass bei jeder Anfrage (jedem Klick auf der Seite) die ganze Seite neu generiert und dem Browser zugeschickt werden muss. Was sich mittlerweile durch AJAX bzw. Web 2.0 vermeiden lässt - ist aber meist viel Programmieraufwand.

    Aber wie eingangs schon erwähnt - sehr wahrscheinlich handelt es sich um ein "Designproblem" im PHP-Skript ;)
     
  3. pepi

    pepi Cellini

    Dabei seit:
    03.09.05
    Beiträge:
    8.741
    Was leider hochgradig unsinnig ist, da so ein "individueller Austragelink" nicht mehr möglich ist...

    Weist Du wo genau das Performance Nadelöhr entsteht? An 400 Einträgen in der Datenbank kann es ja wohl kaum liegen würde ich sagen. Wenn Du PHP Timeouts bekommst, könntest Du die PHP Timeout Zeit möglicherweise etwas länger stellen so Du das beim Hoster darfst. Ebenso hilft vieleicht die RAM Zuteilung pro PHP Skript ein wenig zu erhöhen (falls möglich).

    Ansonsten würde ich das vielleicht ein wenig aufteilen, und für jeden Anfangsbuchsaben im Alphabet einzeln aufrufen lassen. (Nacheinander quasi) Wobei ich wie gesagt auch vermute, daß es da woanders krankt. An dem bissl Mailen kanns kaum liegen.
    Gruß Pepi
     
  4. tjp

    tjp Baldwins roter Pepping

    Dabei seit:
    07.07.04
    Beiträge:
    3.245
    Hier wird das Problem liegen. Auf was für einer Umgebung läuft das PHP Skript (gehostet beim ISP, oder eigener Server)?
     
  5. AAPL

    AAPL Roter Delicious

    Dabei seit:
    17.08.07
    Beiträge:
    93
    Sensationell wie Ihr Analysen macht ohne eine Zeile des Codes gesehen zu haben ;)
    Helfen kann dir nur wer:

    1. Die Umgebung kennt
    2. Den Code kennt

    alles andere sind Spekulationen.
     
  6. .holger

    .holger Geflammter Kardinal

    Dabei seit:
    13.09.04
    Beiträge:
    9.117
    also die Seite ist bei nodeeps.de gehostet - also kein eigener Server - das können wir uns leider nicht leisten.

    hier der relevante Code:
    Code:
    <?
    if (isset($_REQUEST['abschicken'])){
    	$empfaenger =  $_REQUEST['empfaenger'];
    	$nachricht =  $_REQUEST['nachricht'];
    	$betreff =  $_REQUEST['betreff'];
    	if (isset($_REQUEST['weitere'])) {
    	
    		$string =  $_REQUEST['adressen'];
    		$adressen = explode(",", $string);
    	}
    	$comma_separated = implode("','", $empfaenger);
    	$comma_separated = "'".$comma_separated."'";
    	$gespeicherteadressen = mysql_query("SELECT Email FROM $emailadressen
    	WHERE Art IN(".$comma_separated.") ORDER BY id");
    
    	while ($row = mysql_fetch_row($gespeicherteadressen))
    	{
    	$adressen = array_merge((array)$adressen, (array)$row[0]);
    	}
    	
    	$Trenner = md5(uniqid(time()));
    	$textzumsenden = $nachricht;
    	
    	
    		$Header = "From: XXXXXXXXXXXXXXXXX"; 
    		$Header .= "\n"; 
    		$Header .= "MIME-Version: 1.0"; 
    		$Header .= "\n"; 
    		$Header .= "Content-Type: multipart/mixed; boundary=$Trenner"; 
    		$Header .= "\n\n"; 
    		$Header .= "This is a multi-part message in MIME format"; 
    		$Header .= "\n"; 
    		$Header .= "--$Trenner"; 
    		$Header .= "\n"; 
    		$Header .= "Content-Type: text/html; charset=utf-8;"; 
    		
    		$Header .= "\n"; 
    		$Header .= "Content-Transfer-Encoding: 8bit;"; 
    		
    		$Header .= "\n\n"; 
    		$Header .= $textzumsenden ."\n\n";
    		$Header .= "--$Trenner"; 
    		
    		if(isset($_REQUEST['anhangfrage'])){
    	 
    		
    		$Header .= "\n"; 
    		$Header .= "Content-Type: ";
    		$Header .= $_FILES['Anhang']['type'];
    		$Header .= "; name=";
    		$Header .= $_FILES['Anhang']['name']; 
    		$Header .= "\n"; 
    		$Header .= "Content-Transfer-Encoding: base64"; 
    		$Header .= "\n"; 
    		$Header .= "Content-Disposition: attachment; filename=";
    		$Header .= $_FILES['Anhang']['name']; 
    		$Header .= "\n\n"; 
    		$Dateiinhalt = fread(fopen($_FILES['Anhang']['tmp_name'], "r"), $_FILES['Anhang']['size']);
    		$Header .= chunk_split(base64_encode($Dateiinhalt));
    		$Header .= "\n"; 
    		$Header .= "--$Trenner--"; 
    		}
    		
    		
    		
    	$i = 0;
    	foreach($adressen as $adresse)
    	{
    	
    	$adresse = str_replace(" ", "", $adresse);
    	
    	$textzumsenden .= "-------------------------";
    	$textzumsenden .= "\n\n";
    	$textzumsenden .= "Um dich abzumelden gehe auf";
    	$textzumsenden .= "\n http://".$_SERVER['HTTP_HOST']."".$_SERVER['PHP_SELF']."?austragen=true&email=".$adresse;
    	
    	mail($adresse, $betreff, "", $Header); 
    
        $i++;
    	
    	}
    		$time = time();
    	$empfaengersave = implode(", ", $empfaenger);
    		if (isset($_REQUEST['weitere'])) {
    	
    		$string =  $_REQUEST['adressen'];
    	}
    	$empfaengersave .= ", ".$string;
    	$eintragen = MYSQL_QUERY("INSERT into $nachrichten (Betreff, Email, Time, Empfaenger) VALUES ('$betreff','$nachricht', '$time', '$empfaengersave')");
    	
    	echo "Es wurden ". $i ." Emails verschickt";
    }
    
     
  7. Hilarious

    Hilarious Gelbe Schleswiger Reinette

    Dabei seit:
    10.08.05
    Beiträge:
    1.759
    Ich habe für eine ganz ähnliche Anforderung das Thema anders gelöst. Daher kann ich Dir raten, dass Du, wenn Du Performance-Probleme hast, durchaus kleinere Einheiten zusammenpacken kannst, so wie Du oben schon selbst überlegt hattest.

    Ich mache es ungefähr so:
    Vor dem Versand wird die Tabelle der Empfänger, die zu dem Zeitpunkt eingeschrieben sind, in mehrere temporäre Tabellen kopiert. Dazu nutze ich nicht die temporären Tabellen von MySQL selbst sondern zu diesem Zweck eigene (dauerhaftere) Temporärtabellen an. Die Primärschlüssel übernehme ich dabei von der Quelltabelle. Möchte sich ein Empfänger abmelden, wird dies dann in der Quelltabelle verzeichnet und nicht in einer dieser Caches.

    Anschließend nehme ich ein E-Mail-Template, in dem ich mir Platzhalter definiert habe und spiele Suchen und Ersetzen mit regulären Ausdrücken. Hier die relevante Zeile (sollte sich selbst erklären):
    Code:
    $this->TextBody	=preg_replace($this->TemplatePatterns, $this->TemplateReplacements, $this->TextBody, -1);
    
    Dabei habe ich die Properties »TemplatePatterns« und »TemplateReplacements« vorher harmonisch als Hashtables angelegt, lese den Text der E-Mail ein, und ersetze in einem Rutsch alle möglichen Fundstellen. So könnten die TemplatePatterns, also die Platzhalter zum Beispiel so aussehen:
    Code:
    [[ANREDE]]
    [[VORNAME]] [[NACHNAME]]
    
    Code:
    $this->TemplatePatterns 	=	array (	0	=>	"/\[\[ANREDE\]\]/i",
    								1	=>	"/\[\[VORNAME\]\]/i",
    								2	=>	"/\[\[NACHNAME\]\]/i",
    								3	=>	"/\[\[DATUM_HEUTE\]\]/i",
    								);
    
    Zusätzlich würde ich Dir empfehlen, grundsätzlich bei E-Mails zwecks gesteigerter Kompatibiltät mit den leidigen »anderen« mit CR/LF und nicht nur mit LF zu arbeiten, also mit »\r\n«.

    Desweiteren könntest Du das array_merge weglassen und die E-Mails innerhalb der Schleife der Abfrage-Ergebnis-Menge zusammenbauen. In Deinem Falle sähe das evtl. so aus:
    Code:
    while ($_detail = mysql_fetch_assoc($result_resource)) {
        TextEinlesen();
        TextErsetzen();
        MailVersenden();
        KontrollAusgabe();
    }
    
     
  8. red

    red Querina

    Dabei seit:
    18.06.07
    Beiträge:
    184
    Ein Modul unseres Content-Management-Systems versendet in Verbindung mit der Pear Mail_Queue Funktion in 5 Minuten bis zu 10.000 E-Mails.

    Im Groben funktionierts so:
    - Newsletter wird generiert und die E-Mails werden in der Datenbank mit "Mail_Queue" gespeichert
    - das Eintragen in die Datenbank erfolgt in 100er oder 500er - Paketen damit kein Server Time Out entsteht ... gelöst wird das mit Cookies und einem automatischen Reload nach jedem Paket ... dies geht aber sauschnell, da die Pear Funktion sehr leistungsstark ist
    - Bei 10.000 E-Mails dauert das ca. 2 - 5 Minuten ... die Datenbank hat dann schon mal 100 MB, aber das ist kein Problem
    - Für den User des CMS ist der "Versand" nun abgeschlossen, der eigentliche E-Mail-Versand erfolgt nun im Hintergrund wiederum in 500er Paketen per CronJob ... 1/4 stündlich ... 2.000 E-Mails pro Stunde ... die Datenbank wird dabei wieder geleert
    - solls mal schnell gehen, kann das Script auch manuell ausgeführt werden und die 10.000 Mails auch in 15 - 30 Minuten versendet werden

    ... dies nur mal als Denkanstoss ... hab das ganze in einem Tag programmiert gehabt und es läuft hervorragend
     
  9. tjp

    tjp Baldwins roter Pepping

    Dabei seit:
    07.07.04
    Beiträge:
    3.245
    Am sinnvollsten wäre es natürlich, wenn man ein externes Skript anstoßen könnte. Aber bei einem Hoster wird das wohl schwierig werden, da Sicherheitsgründe dagegen sprechen.
     
  10. Hilarious

    Hilarious Gelbe Schleswiger Reinette

    Dabei seit:
    10.08.05
    Beiträge:
    1.759
    Auch eine sehr schöne Lösung. Einziger Wermutstropfen: Manche E-Mail-Provider (wie zB web.de) blockieren Dich, wenn Du mehr E-Mails als üblich in kurzer Zeit bei Ihnen anlieferst.
     
  11. red

    red Querina

    Dabei seit:
    18.06.07
    Beiträge:
    184
    Stimmt leider, aber zum Glück umfassen die Verteiler der Kunden max. 2.000 E-Mail-Adressen und das Versenden im Hintergrund dauert automatisch ja doch ne Weile.
    Reports über jeden Versand geben dem Kunden Aufschluss wer den Newsletter bekommen, geöffnet und evtl. Links im Newsletter angeklickt hat.
    Unser Tool ist nicht mit diesen MonsterNewsletterApplikationen zu vergleichen die Unsummen kosten und im Grunde nur zu 1/5 genutzt werden, ein Versand an 10.000 E-Mail Adressen aber ohne Probleme möglich.
     
  12. ruelpsnase

    ruelpsnase Tokyo Rose

    Dabei seit:
    07.07.07
    Beiträge:
    71
    also an dem Quelltext konnte ich bis jetzt nix Verfängliches entdecken. Wenn Du die Chance hast, dass zu Debuggen, dann versuche doch 'mal festzustellen, welche Stelle tatsächlich so lahm wird. Kann ja durchaus sein, dass einfach nur der "mail()"-Befehl langsam ist, weil der Hostrechner das (wie Vorredner schon angedeutet haben) künstlich verlangsamt.
    Wenn der Debug nicht möglich ist, dann versuche hinter allen relevanten Schritten Datum und Uhrzeit in eine Datei zu schreiben und sieh dir hinterher an, was am meisten Zeit gekostet hat.

    Also ersteinmal genau feststellen, wo das PHP-Pferd wirklich lahmt.
     
  13. rok°!

    rok°! Gala

    Dabei seit:
    26.10.04
    Beiträge:
    48
    Hallo!
    Das Performance Problem besteht bei der Auslieferung an den Mail-Dienst. Das dauert i.d.R. bis zu max. 2 Sekunden pro Mail (je nach Mailgröße und Hardwareausstattung des Servers). Markiere vor dem Versenden die Mails die zu versenden sind und lösche die Markierung nach jedem erfolgreichen Versand an den Empfänger. Dann kannst du auch nach einem Abbruch an die Leute weiter versenden, die noch keinen Newsletter bekommen haben.

    Alternativ kannst du noch folgende Option einbringen, welche die Seite nach (meinetwegen) 50 versendeten Mails einfach neu lädt. Anhand der oben erklärten Funktionsweise, weiß dann das Skript, wo es weiter machen soll. Ein Seiten-Reload bedeutet auch den Neustart des PHP-Skripts. Somit wird zumindest nicht vom Server abgebrochen und die Mails werden sauber versendet.

    *hust*
    Oder du probierst was aus, welches das oben erwähnte schon alles kann *mitzaunpfahlaufsignaturzeig* :)
     

Diese Seite empfehlen