Mastershrimp: Newsletterversand mit usleep(1) = gut?

Heyho!

Ich plane momentan für ein Projekt eine Newsletter-Funktion zu basteln. Da ich momentan noch nicht weiß, wie groß die Anzahl der Empfänger ist, würde ich gerne von vornherein vom schlimmsten Fall ausgehen, d.h. so viele User, dass die mail()-Funktion ein Timeout verursacht.

Um dieses Timeout zu verhindern gibt es 3 Möglichkeiten:

  1. Stückchensweise die Adressen auslesen und verschicken:
     Problem: Wo ist die Grenze? Hängt die Laufzeit nicht auch von der Größe des Mailtextes ab? Außerdem ist diese Methode verhältnismäßig kompliziert

  2. Verwenden von BCC: Bringt das überhaupt was? Kann dadurch ein Timeout verhindert werden?

  3. Unterdrücken des Timeouts per usleep(1):
    Und nun zu meiner eigentlichen Frage. In einem Thread im Archiv (http://forum.de.selfhtml.org/archiv/2004/2/71592/#m412176) wurde diese Lösung diskutiert und in meinen Augen sieht sie recht einfach und effektiv aus.
    Funktioniert diese Methode wirklich?

Würde so etwas funktionieren:

for($i=0; $i<10000; $i++) {
 mail(...);
 usleep(1);
}

Danke schonmal im Vorraus!

Gruß

Mastershrimp

--
Kämpft für die Rettung von dem Genitiv!
  1. moin!

    am einfachsten wird es sein, wenn du die mail an jeden einzelnen user versendest. beim versenden markierst du einfach die user, an die erfolgreich versendet wurde. sollte das script abbrechen, kannst du dann an dieser stelle neu ansetzen.

    die methode hat allerdings den nachteil, das du zusehen musst. d.h. die ganze aktion läuft in deinem browserfenster ab, bis alle mails versendet wurden.

    der rest ist nur bastelei und wird sicherlich auf dem ein oder anderen system nicht 100%ig laufen.

    gruß.
    roger.

    --
    Dein eigenes Newslettersystem auf deiner Homepage: http://newsletter.maennchen1.de
  2. Hallo Mastershrimp,

    ehrlich, hab ich noch nie gemacht. Aber:

    Um dieses Timeout zu verhindern gibt es 3 Möglichkeiten:

    1. Stückchensweise die Adressen auslesen und verschicken:
       Problem: Wo ist die Grenze? Hängt die Laufzeit nicht auch von der Größe des Mailtextes ab? Außerdem ist diese Methode verhältnismäßig kompliziert

    Warum soll das kompliziert sein? Du koenntest bspw. deine Mailliste in Brocken à 100 Stueck abarbeiten und dann per Header-Relocation auf den naechsten Brocken verweisen, also:
    sendNewsletter.php?start=0&size=100
    => sendNewsletter.php?start=100&size=100
    => sendNewsletter.php?start=200&size=100
    Und zum Schluss, wenn das Ende der Liste erreicht ist, machst du deine Ausgabe ("erfolgreich 100.000 begeisterte User erreicht").

    Naja, nur so ein Gedanke...

    Uebrigens wuerde mich interessieren, wie du den Newsletter implementierst. Mit HTML-Versand? Oder benutzt du was Fertiges?

    Eddie

    --
    Old men and far travelers may lie with authority.
    1. moin!

      Warum soll das kompliziert sein? Du koenntest bspw. deine Mailliste in Brocken à 100 Stueck abarbeiten und dann per Header-Relocation auf den naechsten Brocken verweisen, also:

      yo, und was ist, wenn plötzlich das versenden einer oder mehrerer mails länger dauert und somit dann die max_execution_time überschritten wird?

      gruß.
      roger.

      --
      Dein eigenes Newslettersystem auf deiner Homepage: http://newsletter.maennchen1.de
      1. Hallo Roger,

        yo, und was ist, wenn plötzlich das versenden einer oder mehrerer mails länger dauert und somit dann die max_execution_time überschritten wird?

        Also bei meinem Provider habe ich zwei Sekunden Rechenzeit pro Script zur Verfuegung. Und der definiert die zwei Sekunden als genutzte "CPU-Zeit", es kann also mit Pausen, auch eine Minute dauern. Und sollte in Hinsicht auf reine CPU-Zeit beim Mail-Versand nicht eine relativ statische Durchschnittszeit erwartet werden koennen? Immerhin ist ja der Inhalt immer derselbe.
        Problematischer wird es da sicherlich bei Datenbank-Zugriffen oder Bildverarbeitung, wo die benoetigte Zeit stark variieren kann.

        Eddie

        --
        Old men and far travelers may lie with authority.
        1. moin!

          Problematischer wird es da sicherlich bei Datenbank-Zugriffen oder Bildverarbeitung, wo die benoetigte Zeit stark variieren kann.

          na siehste. wo holt er denn die adressen her? :)

          gruß.
          roger.

          --
          Dein eigenes Newslettersystem auf deiner Homepage: http://newsletter.maennchen1.de
          1. Heyho!

            Problematischer wird es da sicherlich bei Datenbank-Zugriffen oder Bildverarbeitung, wo die benoetigte Zeit stark variieren kann.

            na siehste. wo holt er denn die adressen her? :)

            Eben. Das ist gerade das Problem. Die sind in einer DB...

            Ich versuche einfach mal so gut es geht die Fragen zu beantworten:

            Warum soll das kompliziert sein? Du koenntest bspw. deine Mailliste in Brocken à 100 Stueck abarbeiten und dann per Header-Relocation auf den naechsten Brocken verweisen

            Klar. So würde das gehen. Allerdings müsste man wissen, wie man diese Brocken dimensioniert. Wenn 1 Brocken = 100 User ist, kann das bei großen Mails zu viel sein.
            Deshalb erschien mir die usleep()-Methode als die bessere Methode.

            Uebrigens wuerde mich interessieren, wie du den Newsletter implementierst. Mit HTML-Versand? Oder benutzt du was Fertiges?

            Ich weiß nicht, ob ich dich jetzt richtig verstanden habe, aber ich würde erstmal nur einen reinen Textversand einbauen. HTML kann der Kunde eh nicht. Außerdem geht reiner Text schneller.

            am einfachsten wird es sein, wenn du die mail an jeden einzelnen user versendest.

            Genau das möchte ich eigentlich nicht. Ich plane ein Vollautomatisiertes Script, das sich nicht ab einer gewissen Anzahl an Usern aufhängt.

            Weiß niemand, ob das mit usleep(1) klappen würde, bzw. welche Nachteile es dabei gibt?

            Gruß

            Mastershrimp

            --
            Kämpft für die Rettung von dem Genitiv!
          2. Hallo Roger,

            na siehste. wo holt er denn die adressen her? :)

            Naja, stimmt schon. Nur will er ja hoffentlich keine Mails fuer www.WorldOfSpam.invalid verschicken, womit seine DB-Zugriffe eher trivialer Natur sein werden. Aber stimmt schon, man muss zukunftsorientiert planen :-)

            Eddie

            --
            Old men and far travelers may lie with authority.
      2. Hello,

        Warum soll das kompliziert sein? Du koenntest bspw. deine Mailliste in Brocken à 100 Stueck abarbeiten und dann per Header-Relocation auf den naechsten Brocken verweisen, also:

        yo, und was ist, wenn plötzlich das versenden einer oder mehrerer mails länger dauert und somit dann die max_execution_time überschritten wird?

        Darum ging das nicht bei usleep(1), sondern es wurde ein Bug von PHP genutzt, um die MaxExecutionTime auszutricken. Nach dem Aufruf von usleep(1) fing die nämlich immer wieder von vorne an zu zählen.

        Der Bug ist aber inzwischen behoben, und man nimmt set_time_limit(0), sofern man dazu berechtigt ist. Das war nämlich der Sinn des BugRiding, das Verbot des save_mode zu umgehen.

        Harzliche Grüße aus http://www.annerschbarrich.de

        Tom

        --
        Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
        Nur selber lernen macht schlau
        1. Heyho!

          Der Bug ist aber inzwischen behoben, und man nimmt set_time_limit(0), sofern man dazu berechtigt ist. Das war nämlich der Sinn des BugRiding, das Verbot des save_mode zu umgehen.

          Also funktioniert das gar nicht mehr?
          Hmm. Dann muss ich wohl oder übel die erste Methode nehmen und die Daten stückchensweise aus der db lesen.

          Ich glaube kaum, dass man als normaler all-inkl.com bzw. 1&1 Nutzer Zugriff auf diese set_time_limit(0)-Sache hat, oder?

          Gruß

          Mastershrimp

          --
          Kämpft für die Rettung von dem Genitiv!
          1. Hello,

            Ich glaube kaum, dass man als normaler all-inkl.com bzw. 1&1 Nutzer Zugriff auf diese set_time_limit(0)-Sache hat, oder?

            Das kann ich Dir nicht sagen. Aber Deine Funktion phpino() kann das wissen. Schick doch mal einen Link auf ein Script, in dem

            <?php  ### info.php ###
            phpinfo();
            ?>

            drinsteht. Nicht mehr und nicht weniger.

            Harzliche Grüße aus http://www.annerschbarrich.de

            Tom

            --
            Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
            Nur selber lernen macht schlau
            1. http://mastershrimp.com/phpinfo.php

              An welchen Werten/Variablen siehst du, ob die Funktion aktiviert ist?

              Was ist, wenn ich Zugriff auf sie habe? Könnte ich nicht mit set_time_limit(0) alles kaputt machen? Sagen wir mal bei einer Endlosschleife? Die würde dann ja immer weitergehen.

              Sowas würde meinem Provider sicher nicht gut gefallen....

              Gruß

              Mastershrimp

              --
              Kämpft für die Rettung von dem Genitiv!
              1. Hello,

                http://mastershrimp.com/phpinfo.php

                An welchen Werten/Variablen siehst du, ob die Funktion aktiviert ist?

                Das ist schon etwas komplexer:

                http://de2.php.net/manual/en/features.safe-mode.functions.php
                Set_time_limit() has no effect, when php is running in safe_mode

                Dein Safe_Mode ist allerdings nicht aktiviert. Safe_mode_gid steht zwar auf on, dass ist aber dem Schalter "Safe_Mode" untergeordnet und hat nur dann Wirkung, wenn Safe_Mode sselbwer auf ON steht. Und zwar wird der Safe_Mode dadurch etwas gelockert.

                Aus Sicht des Safe_Mode ist set_time_limit() also nicht beeinträchtigt.

                Weitere Möglichkeit ist "disable_functions". Dort werden alle Funktionen aufgezählt, die Du nicht benutzen darfst. Allerdings sind dort auch keine Werte eingetragen.

                Also folgere ich daraus, dass Dein Script laufen muss, bis zum Abwinken.

                Was ist, wenn ich Zugriff auf sie habe? Könnte ich nicht mit set_time_limit(0) alles kaputt machen? Sagen wir mal bei einer Endlosschleife? Die würde dann ja immer weitergehen.

                Darum predige ich ja immer, dass man sich, wenn man endlos und/oder Backgroundprozesse schreibt die PID merken soll. http://de2.php.net/manual/en/ref.info.php und darin speziell http://de2.php.net/manual/en/function.getmypid.php

                Diese Process ID sollte man sich also irgendwo merken, bovor das Script vielleicht in einen unvorhergesehenen Zustand eintritt. Wenn Du dann später z.b. mit system() und 'ps -aux' nachschaust, ob der Prozess noch lebt, kannst Du ihn ggf. mit system() und kill (ob wwwrun das darf weiß ich im Moment nicht) beseitigen, oder auch dem Provider eine Mail schicken. Um den Prozess zu identifiziern musst Du Dir auch die Startzeit merken! Die PIDs sind keine verlorenen Schlüssele, sondern revolvierende.

                Sowas würde meinem Provider sicher nicht gut gefallen....

                Dann soll er seinen Server ordentlich einrichten. Die Verwendung von Funktionen bereitgestellter Software sind immerhin bestimmungsgemäßer Gebrauch.

                Der Server ist ohnehin sehr unsicher eingerichtet. Du kannst das Verzeichnis /tmp lesen (open_basedir). Darin sind auch (siehe session.save_path) die Sessions gespeichert. Nun kannst Du mittels Deines Scriptes wahrscheinlich das Verzeichnis /tmp/ auslesen. Du müsstest also die Sessions aller anderen Benutzer lesen dürfen.

                Da diese Sessions als Dateinamen "sess_$sid" tragen, hast Du also die Session-Nummer und die in der Session gespeicherten Daten zur Verfügung.

                Ich würde Dir also dringend anraten DEINE Sessions innerhalb Deines eigenen Userbereiches zu speichern. Das Verzeichnis kannst Du dann ja mit .htaccess schützen, sollte das innerhalb der DocRoot sein müssen.

                Wer ist Dein Provider? (kasserver.com, wer ist das?)

                Harzliche Grüße aus http://www.annerschbarrich.de

                Tom

                --
                Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                Nur selber lernen macht schlau
                1. Heyho!

                  Uff. Das war viel.
                  Sofern ich alles richtig verstanden habe, müsste die Funktion bei mir laufen. Ich könnte also problemlos sie erlaubte Laufzeit ins Unendliche schrauben.

                  Damit das Script dann aber keinen Mist baut, sollte ich das mit system() und der PID kontrollieren...soweit noch klar. Aber wie kann ich das kontrollieren? Und wie kann ichs abbrechen?

                  Ich glaube ich mach doch die "Stückensweise-auslesen-und-verschicken"-Methode. Die erscheint mir im Nachhinein doch einfacher. Mit system() und dem ganzen internen Kram kann ich kaum was anfangen.

                  Danke auch für die ausführliche Beschreibung der Safe-Modes-Einstellungen.

                  Gehe ich recht in der Annahme, dass das Script, das du mir per Mail geschickt hast den tmp-Ordner auslesen soll?

                  Wer ist Dein Provider? (kasserver.com, wer ist das?)

                  Das ist all-inkl.com.
                  Die Administration läuft aber irgendwie über dieses KAS-System. Ka was das bedeutet. Hauptsache es läuft ;)

                  Wie gesagt, vielen Dank für deine Mühen, ich werde aber glaub ich erstmal die andere Methode ausprobieren. Die Time-Limit-Methode erscheint mir als sehr unsicher und komplex.

                  Gruß

                  Mastershrimp

                  --
                  Kämpft für die Rettung von dem Genitiv!
                  1. Hello,

                    Uff. Das war viel.
                    Sofern ich alles richtig verstanden habe, müsste die Funktion bei mir laufen. Ich könnte also problemlos sie erlaubte Laufzeit ins Unendliche schrauben.

                    Ja, durch set_time_limit(0) wird die Prüfung ausgeschaltet.

                    Damit das Script dann aber keinen Mist baut, sollte ich das mit system() und der PID kontrollieren...soweit noch klar. Aber wie kann ich das kontrollieren? Und wie kann ichs abbrechen?

                    Genau. mit getmypid() und den anderen Funktionen dieser Familie holst Du Dir gleich am Anfang die Metadaten des Scriptes (PID, PPID, Startzeit) und trägst sie mittels Appendmode in ein Logfile ein.

                    Außerdem kannst Du das Script gelegentlich auch während der Laufzeit noch eine Kontrollmeldung eintragen lassen, z.B.

                    Auftrag 700, PID=29345, Startzeit 2004-09-11 23:01:02, 700 von 7.000 Mails erstellt

                    Dann weißt Du, dass es noch läuft.

                    Um es vom Browsr aus starten zu lassen, musst Du sowieso ignore_user_abort() benutzen, wenn es nach dem Schließen des Browserfensters weiterlaufen soll.

                    Mittels eines zweiten Scriptes oder eben einem intelligenten Affenformular kannst Du dann Dein Logfile einsehen und feststellen, ob da noch was rumgeistert.

                    Mittels system() Funktion und dem Systemprogramm "ps -aux" kannst du die laufenden Prozesse anzeigen lassen. Wenn Deiner (mit der PID, der PPID, der Startzeit) noch vorhanden ist, und keine sinnvollen Kontrollmeldungen mehr im Logfile auftauchen, ist was schiefgegangen.

                    Gehe ich recht in der Annahme, dass das Script, das du mir per Mail geschickt hast den tmp-Ordner auslesen soll?

                    Ja, aber bitte nicht veröffnetlichen, sondern nur einmal ausprobieren, mir das Ergebnis mitteilen und wieder vergessen...

                    Harzliche Grüße aus http://www.annerschbarrich.de

                    Tom

                    --
                    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                    Nur selber lernen macht schlau