Aufbau einer Auftragswarteschlange

| 3 Kommentare

Eigentlich verrät der Titel ja schon fast worum es in diesem Artikel geht. In den vergangenen Wochen hatte ich die Aufgabe, mir ein paar Gedanken zu machen, wie sich die Mailkommunikation mit unseren Kunden besser gestalten lässt. Es wurden viele Anforderungen gesammelt und es kommt immer mal wieder etwas dazu.

Ein Bestandteil des neuen Systems ist die Auftragswarteschlange, deren Prinzip sich nicht nur für den Mailversand anwendbar ist und so den Kern des heutigen Artikels bildet.

Die Anforderungen an das System sind u.A. das Informationen über verschiedene Wege verschickt werden können, der Versand muss zeitlich und mengenmäßig kontrollierbar sein und kann durch verschiedene Ereignissen in Auftrag gegeben werden.

Um das zu erreichen musste der Versendeprozess zuerst soweit vereinheitlicht werden, dass es für alle Mailarten passt. Es enstand eine relativ einfache Schnittstelle, mit der sich die Aufträge verarbeiten lassen. Die Beispiele sind Pseudocode.

<?php
interface Mailtyp
{
  public function queue();
  public function send();
  public function getUniqueKey();
}

Die Methode queue() ist in einer abstrakten Klasse implementiert, von der die konkreten Typen abgeleitet werden und schreibt mit Hilfe eines Models die Aufträge standardisiert in die Datenbank. Dabei werden der Typ, ein unique Key und einige Parameter in serialisierter Form (Json) gespeichert.

Alle weiteren Methoden werden später in der konkreten Klassen implementiert, da diese vom jeweiligen Anwendungsfall abhängig sind.

Ich versuche es mal mit einem Beispiel anschaulicher zu machen. Es soll eine Begrüssungsemail versendet werden, wenn sich ein Kunde anmeldet. Dafür gibt es den Mailtyp ‘welcome’.

<?php
class Mailtype_Welcome extends Mailtype_Abstract
{
  public function getCustomer() { ... }
  public function setCustomer($customer) { ... }

  public function getUniqueKey() { return 'welcome_' . $customerId; }
  public function send() { /* sende Email an $customerEmail */ }
}

Die konkrete Klasse implementiert nun die fehlenden Methoden in Abhängigkeit zum Anwendungsfall. Die Methode getUniqueKey() gibt einen typabhängigen UniqueKey zurück und lässt dadurch für diesen Typen nur einen Auftrag je Kundennummer zu.

In der Anwendung würde dieser Auftrag nach dem Speichern eines neuen Kundendatensatzes erzeugt werden.

<?php
// ...
MailTypeFactory('welcome')->setCustomer($customer)->queue();

Der Datensatz in der Datenbank könnte dann etwa so aussehen:

+----+---------+------------+--------------------+--------+
| id | type    | unique_key | params             | status |
+----+---------+------------+--------------------+--------+
|  1 | welcome | welcome_42 | {"customer_id":42} | NEW    |
+----+---------+------------+--------------------+--------+

Neben der “Willkommens-Email” könnten nun auch noch beliebige andere Aufträge eingehen. Da wir bislang nur einen einfachen Datensatz speichern verursachen wir auch nur recht wenig Last im System.

Für die spätere Verarbeitung der Aufträge wird ein Daemon zuständig sein, der sich in festgelegten Abständen eine Liste von neuen Aufträgen holt und verschickt.

Für das Beispiel macht den Job ein einfaches Script mit einer Endlosschleife.

<?php
while (true) {
  $tasks = loadNewTasks();  // lädt Daten und erzeugt Objekte

  foreach ($tasks as $task) {
    $task->send();
  }

  sleep(1);
}

Die Methode send() in den jeweiligen Objekten entscheidet nun, wie eine Nachricht versendet werden soll. Mit etwas mehr Code lässt sich dadurch der spätere Mailverkehr besser kontrollieren, z.B. läßt sich leicht eine Historie zu einem Kunden anlegen oder was auch immer man benötigt.

Wie gesagt läßt sich dieses Muster für verschiedene Anwendungsfälle implementieren und erweitern. Die Vorteile sind wenig Last beim “Auftraggeber”, zeitversetzte Verarbeitung und auch Priorisierung von Aufträgen.

3 Kommentare

  1. Ja, solch ein System haben wir auch bereits umgesetzt.

    Bei uns ist der Fokus aber nur auf den soliden Mailversand gelegt worden, heißt, wir haben darauf geachtet, dass die Mail (wenn sie denn nicht versandt wurde) nochmal neu versandt wird, etc., aber im Grunde, genau das gleiche Prinzip.

    Die Idee ist in sofern gut, da auch Mails, die z.B. mal nicht ankommen, weil der Zielmailserver gerade nicht erreichbar ist, später dann nochmal erneut verschickt werden.

    Die Idee mit deinem Unique-Key finde ich interessant!

  2. @stietze: “Die Idee ist in sofern gut, da auch Mails, die z.B. mal nicht ankommen, weil der Zielmailserver gerade nicht erreichbar ist, später dann nochmal erneut verschickt werden.” Ich hoffe du meinst den versendenen SMTP, dem die Mail übergeben wird, und nicht den Zielmailserver des Adressaten. Denn gerade dieses Feature mit den Warteschlangen und dem erneuten versenden etc. können “richtige” SMTP Server wie Postfix deutlich besser als ein PHP Script. Falls du erstes meinst, würde ich dir Recht geben.
    Ich überlege gerade ob ich dafür nicht Gearman verwenden würde, also ein verteiltes System, was genau für sowas ausgelegt ist. Ein Client der einen Job/Task erstellt, und 1-x Worker die die Arbeit erledigen.

Hinterlasse eine Antwort

Pflichtfelder sind mit * markiert.


Schlagwörter: A/B-Test, AbstractType, Adapter, AddOn, Administration, Ajax, Alühn, Alühn2, Amazon, Animation, Annotations, Anonyme Klasse, Ant, Apache, API, Array, ArrayAccess, Attachment, Auftrag, Ausbildung, Auswertung, Authentifizierung, AutoLoader, AWS, Backup, Bedienung, Bedingung, Benchmark, Berechtigung, Berlin, Bildbearbeitung, Bildschirmfoto, Blog, Blogroll, BOM, Bootstrap, Bot, Browser, Bugtracker, Byte Order Mark, Bücher, Cache, CakePHP, Call-Center, Callback, CamelCase, Canvas, Captcha, CDN, Cheatsheet, CLI, Clickout, Closure, Cloud, CodeSniffer, Collection, Community, Comparator, Config, Contest, Controller, Converter, CouchDB, Countable, Cronjob, CRUD, CSS, CSV, CustomLibrary, Custom_Model, Daemon, Data Mapper, Datei, Datenbank, Datensicherung, Datenstruktur, Datentypen, Dating, Datum, Debug, Decorator, Dekorierer, Design, Design Patterns, Doctrine, Dokumentation, Dump, Duplikat, each, EC2, Eclipse, Email, Entwicklung, Entwurfsmuster, Enum, Erweiterung, Event, Eventhandling, Exception-Handling, Extension, Facebook, Factory, Fallback, Fehler, Fehlermeldung, Filter, Firefox, Flash, flexigrid, Foreach, Formatierung, Formular, Framework, FTP, Funktion, Futon, ga:pi(), Getter, Gnome, Google Analytics, Hash, Hash-Bang, Header, htaccess, HTML5, htpasswd, HTTP, HTTPS, IDE, If, Implementierung, InnoDB, Interceptor, Interface, Internet Explorer, isset, Iterator, Java, JavaScript, Job, jQuery, Kommentar, Konfiguration, Konsole, Kontrollstruktur, kopieren, kostenlos, Kundenbetreuung, Late Static Binding, Layout, Links, Linux, Listeners, Lizenz, Logging, Löschen, Magento, Magic Methods, Manual, ManyToMany, Marketing, Methode, Model, Monolog, MVC, MySQL, NetBeans, Network, Nirvanix, Objekt, Observable, Observer, OneToMany, Online Tool, OOP, Open Source, Operator, OR-Mapper, Order, ORM, O’Reilly, Parameter, Partnersuche, Passwort, Performance, PHP, php.ini, PHP hates me, phpMyAdmin, PHPUnit, Plugin, Point and Click, Popup, Praktikum, Proxy, Prüfsumme, Prüfung, QR-Code, Qualitätssicherung, Query, Queue, Redesign, Refactoring, Reflection, Repository, Request, Response, Responsive Design, Rest-API, Rockstar, Rollback, Routing, S3, Samba, Scheifen, Schleife, Schutz, Screenshot, Secure Shell, Selbstreferenz, Server, Setter, setTimeout, Shop, Sicherheit, Sicherung, Sichtbarkeit, Singleton Pattern, Skin, SOAP, Social Network, Software, Sortierung, Sourcecode, Spam, Speicherproblem, Spickzettel, SPL, Splittest, SSH, SSL, Stammtisch, Statement, static, Statistik, Status, Stellvertreter, Strategy Pattern, Stream, String, Stuttgart, Stylesheet, Subversion, Sun VirtualBox, Support, SVN, Switch, Symfony, Symfony2, Symfony Live, Tag, Template, Template Method, Ternär Operator, Testing, Theme, Thumbnail, Tool, Tour, Tracking, Twig, Twitter, Type-Cast, Ubuntu, Umwandlung, Underscore, unset, Update, Upload, Url, User Story, Validierung, Vererbung, Versionskontrolle, Versionsnummer, Verzweigung, Video, Videospiel, Virtualisierung, Visitor Pattern, Vorschaubild, walk, Warteschlange, Webserver, Webservice, Weiterleitung, Werkzeug, Windows, WindowsAzure, WordPress, Wrapper, Writer, XML, Youtube, Zeitschleife, Zeitsteuerung, Zend Framework, Zend_Application, Zend_Cloud, Zend_CodeGenerator, Zend_Http_Client, Zend_Reflection, Zend_Service, ZPress, Zugangskontrolle, Zugriffsmethode