Dev Life
Optimierung des Massen-E-Mail-Versands durch benutzerdefiniertes Warteschlangenmanagement mit der Mailgun-API
Der Versand von Massen-E-Mails erfordert einiges an Fingerspitzengefühl und Sorgfalt. Mehrere Faktoren erhöhen die Komplexität. Dazu gehören die Drosselung und Ratenbegrenzungen von E-Mail-Anbietern zur Steuerung des E-Mail-Flusses, einzuhaltende Datenschutzgesetze sowie Probleme bei der Zustellbarkeit aufgrund ungültiger Adressen oder Duplikate. Darüber hinaus müssen Sie SMTP-Verbindungsgrenzwerte, vom Anbieter auferlegte Limits und unerwartete Verzögerungen verwalten.
Glücklicherweise lassen sich diese Herausforderungen meistern. Dieser Leitfaden zeigt Ihnen, wie Sie die API von Mailgun als Taktstock nutzen, um den Massen-E-Mail-Versand zu optimieren und gleichzeitig häufige Fallstricke zu vermeiden. Lassen Sie uns aber zunächst untersuchen, warum diese Probleme überhaupt auftreten – und warum sie Ihre Massen-E-Mail-Kampagnen zum Scheitern bringen.
Die Herausforderungen beim Versand von Massen-E-Mails
Ratenbegrenzungen und Drosselungen durch E-Mail-Service-Provider (ESPs) sind in der Regel die häufigsten Ursachen für Probleme bei der Zustellbarkeit von Massen-E-Mails. Neben den von Anbietern festgelegten Grenzwerten spielt auch Ihre Absenderreputation eine wichtige Rolle. Der Start einer Massen-E-Mail-Kampagne, ohne Ihre Bewertung zu überprüfen und ESP-spezifische Limits zu verstehen, verschlechtert Ihre Reputation schnell.
Eine sinkende Absenderbewertung führt zu einer negativen Rückkopplungsschleife, bei der jeder Bounce oder jede Platzierung im Spam-Ordner Ihre Bewertung senkt, was die Zustellbarkeit weiter beeinträchtigt. Mit sinkender Bewertung ist die Wahrscheinlichkeit höher, dass ESPs Ihre Domain(s) oder IP-Adresse markieren oder auf eine Blockliste setzen.
Auch wenn dies bei internen Benachrichtigungssystemen kein Problem darstellt, stören Verzögerungen bei der Zustellbarkeit (und wie Sie diese handhaben) fast jeden Anwendungsfall für Massen-E-Mails. Netzwerküberlastungen, aggressive Spamfilter und Serverprobleme verlangsamen die Zustellung, was besonders bei zeitkritischen Warnungen problematisch ist. Diese Verzögerungen führen zudem zu doppelten E-Mails, wenn Systeme nicht zugestellte Nachrichten automatisch erneut versenden. Das erhöht das Risiko einer Platzierung im Spam-Ordner und schadet Ihrer Absenderreputation weiter.
Glücklicherweise lassen sich viele dieser Probleme beheben, indem Sie besser kontrollieren, wie und wann Ihre Massen-E-Mail-Nachrichten versendet werden. Anstatt Ihre Nachrichten unkontrolliert zu versenden, benötigen Sie einen intelligenteren Ansatz, der wichtige Variablen berücksichtigt. Hier kommen Warteschlangen ins Spiel.
So helfen Warteschlangen
Wenn Sie sich um Ihre Absenderreputation sorgen: Warteschlangen korrigieren diese nicht direkt oder sofort, helfen aber bei der Verbesserung.
Warteschlangen sind Datenstrukturen, mit denen Sie Ihre Nachrichten programmatisch sortieren, kategorisieren und anschließend nacheinander verarbeiten. So koordinieren und steuern Sie die Versandraten Ihrer Massen-E-Mail-Kampagnen.
Sie haben beispielsweise die Möglichkeit, Ihre Massen-E-Mails nach E-Mail-Anbieter aufzuteilen, für jede Gruppe eine eigene Warteschlange einzurichten und dann Regeln für die Verarbeitung Ihrer Warteschlangen festzulegen. Dies ist nützlich, wenn Sie E-Mails während verkehrsarmer Zeiten planen müssen, um die Zustellbarkeit zu erhöhen.
Warteschlangen unterstützen auch den Lastausgleich, da Sie kurze Intervalle oder Pausen zwischen der Verarbeitung einzelner E-Mail-Gruppen einbauen. Außerdem speichern Sie ausgehende Nachrichten vorübergehend zwischen, sodass der Server Zeit hat, jede Last zu verarbeiten.
Warteschlangensysteme machen auch den Umgang mit nicht zugestellten E-Mails effizienter. Sie richten intelligente Strategien für den erneuten Versand ein. So versuchen Sie den Versand von Nachrichten, die aufgrund von Netzwerk- oder Serverfehlern fehlgeschlagen sind, erneut, während Sie solche mit ungültigen Adressen oder vollen Postfächern überspringen. Zudem nutzen Sie Ihre Warteschlangen, um eine Bounce-Liste einzurichten und unzuverlässige Empfängeradressen herauszufiltern.
Darüber hinaus optimieren Warteschlangen Massen-E-Mail-Kampagnen, indem sie die Leistungsüberwachung vereinfachen. Da sie die Steuerung des Flusses ausgehender E-Mails ermöglichen, erleichtern sie das Erkennen von Engpässen, das Verfeinern Ihrer Strategie sowie die Fehlerbehebung.
Optimierung des Massen-E-Mail-Versands mit Warteschlangen und Mailgun
Stellen Sie sich für diesen Leitfaden vor, Sie entwickeln einen Benachrichtigungsservice für die Belegschaft eines großen globalen Unternehmens mit Personal und Niederlassungen in verschiedenen Regionen weltweit. Einige Nachrichten sind unternehmensweit, während andere standort- oder abteilungsspezifisch sind.
Warteschlangenlösung auswählen
Zunächst müssen Sie herausfinden, welche Tools und Komponenten Sie für dieses Projekt benötigen. Für das Warteschlangenmanagement haben Sie folgende Optionen:
- RabbitMQ ist ein Open-Source-Message-Broker mit flexiblem Routing und mehreren Protokollen. Dank einer aktiven Community bietet er Client-Bibliotheken und Entwicklertools für Java, .NET, Erlang, Python, PHP, JavaScript und Go.
- Amazon SQS ist eine hervorragende Option, wenn Sie bereits im AWS-Ökosystem verankert sind oder eine vollständig verwaltete Lösung wünschen. Die größten Pluspunkte sind der Support für verteilte Warteschlangensysteme und die Skalierbarkeit.
- Apache Kafka ist eine vielseitige, quelloffene, verteilte Daten-Streaming- und Verarbeitungsplattform, die als Message-Broker genutzt wird. Da die Lösung für die Verarbeitung großer Mengen an Echtzeitdaten entwickelt wurde, bietet sie einen höheren Durchsatz als die meisten herkömmlichen Warteschlangen- oder Message-Broker. Die Konfiguration und Verwaltung von Kafka gestaltet sich jedoch aufgrund der Komplexität mitunter als Herausforderung.
- Redpanda ist eine vereinfachte und weniger ressourcenintensive Alternative zu Kafka. Es ist mit den APIs von Kafka kompatibel und bietet Support für viele der gleichen Implementierungen, einschließlich Message-Brokering.
Da dies ein relativ großes Projekt werden dürfte, ist eine verwaltete Lösung mit automatischer Skalierung – wie SQS – vorzuziehen. Wenn Sie jedoch über die Erfahrung und Ressourcen für die Eigenverwaltung verfügen, senkt eine Open-Source-Lösung Kosten und bietet mehr Anpassungsmöglichkeiten.
Aber anstatt das E-Mail-Management selbst zu übernehmen, ist eine intelligentere Lösung Sinch Mailgun. Diese vereinfacht nicht nur die Zustellung der E-Mails, sondern ermöglicht auch die Erstellung benutzerdefinierter Warteschlangen. So behalten Sie die volle Kontrolle, ohne sich um die Verwaltung der Infrastruktur kümmern zu müssen.
Überblick über die Architektur
Die nächsten Abschnitte zeigen Ihnen, wie Sie programmatisch benutzerdefinierte Warteschlangen erstellen, um Ihre Massen-E-Mail-Kampagnen zu verwalten. Dieses Beispiel verwendet RabbitMQ für die Erstellung und das Management von Warteschlangen sowie das Sinch Mailgun Java SDK , um E-Mail-Nachrichten asynchron zu versenden. Da RabbitMQ und Sinch Mailgun Support für eine Vielzahl von Programmiersprachen und Frameworks bieten, passen Sie dieses Beispiel problemlos an Ihre bevorzugte Programmiersprache an.
Neben der HTTP-API und dem Java SDK bietet Sinch Mailgun sprachspezifische Software Development Kits für Go, Node.js, PHP und Ruby.
Das folgende Architekturdiagramm (sowie die anschließende Erklärung) hilft Ihnen, die Struktur und Funktionalität des Systems für Warteschlangen und Massen-E-Mail-Versand besser zu verstehen:

Dieses System verwendet eine Mailgun-API von Sinch (MailgunMailingListApi), um alle Mailinglisten abzurufen, die mit Ihrem Konto verbunden sind. Mailinglisten lassen sich programmatisch über einen Client oder eine Anwendung zur Nutzerregistrierung oder manuell über das Sinch Mailgun Dashboard. erstellen und füllen. Mit Mailinglisten organisieren und verwalten Sie Ihre E-Mail-Empfänger strukturiert. Sie dienen zudem als Datenquelle Ihres Systems.
Sobald das System alle Mailinglisten abgerufen hat, wird für jede eine RabbitMQ-Warteschlange erstellt. Die Warteschlangen werden mit den E-Mail-Inhalten sowie dem Namen und der Adresse jedes Empfängers (Mitglieds) gefüllt, der die E-Mail erhält.
Ein separates Modul, das geplante Ausführungsroutinen für jede Warteschlange enthält, sucht in bestimmten Intervallen nach neuen Elementen in den Warteschlangen. Sobald eine der Ausführungsroutinen neue Elemente in der Warteschlange erkennt, beginnt sie mit deren Verarbeitung und arbeitet die Warteschlange ab. Im letzten Schritt verwendet der E-Mail-Prozessor des Systems die MailgunMessagesApi, um die Nachricht zu senden.
Sie erhöhen die Robustheit des Systems, indem Sie eine Warteschlange oder Datenbank hinzufügen, um Nachrichten zu verwalten, deren Versand fehlgeschlagen ist, und erfolgreiche Nachrichten zu archivieren.
Eigene Warteschlangen erstellen
Um eine Warteschlange zu erstellen, konfigurieren und starten Sie eine RabbitMQ-Instanz mit Docker. . Danach erstellen Sie ein neues Java-Projekt für die Erstellung von Warteschlangen. Fügen Sie der Abhängigkeitsliste Ihres Projekts unbedingt folgende Bibliotheken hinzu:
- In der RabbitMQ amqp-client
- Mailgun
- In der Simple Logging Facade for Java
Fügen Sie außerdem folgende Importe zu Ihrer Hauptklasse hinzu:
// Mailgun importsrnimport com.mailgun.api.v3.MailgunMailingListApi;rnimport com.mailgun.client.MailgunClient;rnimport com.mailgun.model.mailing.lists.MailingListData;rnimport com.mailgun.model.mailing.lists.MailingListMember;rnrn// RabbitMQ importsrnimport com.rabbitmq.client.AMQP;rnimport com.rabbitmq.client.ConnectionFactory;rnimport com.rabbitmq.client.Connection;rnimport com.rabbitmq.client.Channel;rnrn// Java importsrnimport java.io.IOException;rnimport java.util.HashMap;rnimport java.util.Map;rnimport java.util.concurrent.TimeoutException;rn
Ihre main-Methode wird mit Code gefüllt, um die Mailinglisten-API von Sinch Mailgun zu initialisieren, eine Verbindung zu RabbitMQ herzustellen und eine Warteschlange für jede Mailingliste zu erstellen:
public static void main(String[] args) throws IOException, TimeoutException{rn // Initialize Mailing List APIrn // Replace with your own Sinch Mailgun API keyrn MailgunMailingListApi mailgunMailingListApi = MailgunClient.config("<SINCH_API_KEY>")rn .createApi(MailgunMailingListApi.class);rnrn // Initialize and create connection to RabbitMQ rn ConnectionFactory factory = new ConnectionFactory();rn // Replace with your detailsrn factory.setHost("<HOSTNAME>"); rn factory.setUsername("<USERNAME>");rn factory.setPassword("<PASSWORD>");rn factory.setPort(<PORT>);rnrn // Create queue channel - throws IOException and TimeoutExceptionrn Connection connection = factory.newConnection();rn Channel channel = connection.createChannel(); rnrn // Loop through mailing listsrn for(MailingListData mailingList : mailgunMailingListApi.getMailingList().getItems()) {rn String queue_name = mailingList.getName();rnrn // Create a simple queue using the mailing list name rn channel.queueDeclare(queue_name, false, false, false, null);rnrn // Populate queue with mailing list member addresses and message contentrn for(MailingListMember mailingListMember : mailgunMailingListApi.getMailingListMembers(mailingList.getAddress()).getItems()) {rn // Create headers with email message detailsrn Map<String, Object> headers = new HashMap<String, Object>();rn headers.put("address", mailingListMember.getAddress());rn headers.put("name", mailingListMember.getName());rn headers.put("subject", "Alert"); rn // Add a row to the queue rn // Replace with actual email contentrn String emailMessage = "Email content"; rn channel.basicPublish("", queue_name,rn new AMQP.BasicProperties.Builder()rn .headers(headers)rn .build(),rn emailMessage.getBytes());rnrn // Add code to log each queued entryrn System.out.println("Sent: " + emailMessage);rn } rnrn }rnrn}
Sobald die erforderlichen Verbindungen hergestellt sind, generiert der Code anhand des Namens eine Warteschlange für jede Mailingliste. Eine for-each-Schleife ruft jedes Mitglied (MailingListMember) der entsprechenden Mailingliste ab. Anschließend verwendet der Code die Daten jedes Mitglieds (Adresse und Name) zusammen mit den Details der E-Mail-Nachricht (Betreff und Nachrichteninhalt), um einen Warteschlangeneintrag zu erstellen und zu veröffentlichen.
An diesem Punkt haben Sie die Möglichkeit, Code hinzuzufügen, um weitere Warteschlangen für Nachrichten- oder Betrefftypen zu erstellen. Richten Sie beispielsweise separate Warteschlangen für Newsletter, Benachrichtigungen, Warnungen und andere wichtige Updates ein. So erhalten Sie eine detailliertere Kontrolle darüber, wie Ihre Nachrichten priorisiert und versendet werden. Der Einfachheit halber lassen Sie diesen Schritt jedoch aus.
Sobald der Code (erfolgreich) ausgeführt wird, sehen und verwalten Sie Ihre Warteschlangen im RabbitMQ-Dashboard:

Nun benötigen Sie einen Client, der die Warteschlangen verarbeitet und Ihre Nachrichten versendet.
Warteschlangen verarbeiten und Nachrichten versenden
Genau wie beim vorherigen Modul fügen Sie auch bei diesem Modul den RabbitMQ amqp-client, SLF4J und die Mailgun-Bibliotheken als Abhängigkeiten hinzu. Fügen Sie zudem die neueste Feign Core-Bibliothek sowie die folgenden Importe zur Hauptklasse Ihres neuen Moduls hinzu:
// RabbitMQ importsrnimport com.rabbitmq.client.Channel;rnimport com.rabbitmq.client.ConnectionFactory;rnimport com.rabbitmq.client.Connection;rnimport com.rabbitmq.client.DeliverCallback;rnrn// Mailgun importsrnimport com.mailgun.api.v3.MailgunMessagesApi;rnimport com.mailgun.client.MailgunClient;rnimport com.mailgun.model.message.Message;rnimport com.mailgun.model.message.MessageResponse;rnrn// Feign Core importsrnimport feign.AsyncClient;rnimport feign.Client;rnrn// Java importsrnimport java.util.concurrent.ScheduledExecutorService;rnimport java.util.concurrent.TimeUnit;rnimport java.io.IOException;rnimport java.nio.charset.StandardCharsets;rnimport java.util.Map;rnimport java.util.concurrent.CompletableFuture;rnimport java.util.concurrent.ExecutorService;rnimport java.util.concurrent.Executors;rnimport java.util.concurrent.TimeoutException;rn
Als Nächstes überlegen Sie, wie Ihre Warteschlangen verarbeitet werden sollen, und legen die entsprechenden Regeln fest. Wenn jede Warteschlange beispielsweise in unterschiedlichen stündlichen Intervallen verarbeitet werden soll, richten Sie einen ScheduledExecutorService ein und erstellen ihn:
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);rnrn public void startProcessing() {rn Runnable china_task = () -> {rn try {rn consumeEmails("China");rn } catch (Exception e) {rn e.printStackTrace();rn }rn };rnrn Runnable britain_task = () -> {rn try {rn consumeEmails("Britain");rn } catch (Exception e) {rn e.printStackTrace();rn }rn };rnrn Runnable america_task = () -> {rn try {rn consumeEmails("America");rn } catch (Exception e) {rn e.printStackTrace();rn }rn };rnrn // Schedule each task to run at different hourly intervalsrn scheduler.scheduleWithFixedDelay(china_task, 11, 11, TimeUnit.HOURS);rn scheduler.scheduleWithFixedDelay(britain_task, 13, 13, TimeUnit.HOURS); rn scheduler.scheduleWithFixedDelay(america_task, 17, 17, TimeUnit.HOURS); rn }rnrn public void stopProcessing() {rn scheduler.shutdown();rn }
Denken Sie daran, den oben genannten Code in die main-Klasse Ihres neuen Moduls einzufügen.
Da die Beispielanwendung über drei Warteschlangen verfügt, instanziieren Sie ein ScheduledExecutorService-Objekt mit einer Kern-Thread-Pool-Größe von drei. Anschließend erstellen Sie eine Aufgabe und legen ein Verzögerungsintervall für jede Warteschlange fest.
In diesem Beispiel ist jede ausführbare Aufgabe nach einer Warteschlange (Mailingliste) benannt. Sie haben jedoch die Möglichkeit, Ihre Aufgaben beliebig zu benennen. Die Methode stopProcessing beendet den Scheduler, wenn Sie die Anwendung schließen.
Nun erstellen Sie eine Methode zur Verwaltung Ihrer Warteschlangen:
public void consumeEmails(String queueName) throws IOException, TimeoutException {rnrn ConnectionFactory factory = new ConnectionFactory();rn // Replace with your detailsrn factory.setHost("<HOST>"); rn factory.setUsername("<USERNAME>");rn factory.setPassword("<PASSWORD>");rn factory.setPort(<PORT>);rnrn Connection connection = factory.newConnection();rn Channel channel = connection.createChannel();rnrn channel.queueDeclare(queueName, false, false, false, null);rn // Send the messagern DeliverCallback deliverCallback = (consumerTag, delivery) -> {rn String emailMessage = new String(delivery.getBody(), StandardCharsets.UTF_8);rn Map <String, Object> headers = delivery.getProperties().getHeaders();rnrn String recipient = headers.get("address").toString();rn String subject = headers.get("subject").toString();rn String name = headers.get("name").toString();rn String body = "Dear " + name + ",
" + emailMessage;rnrn sendEmail(recipient, subject, body);rn };rn channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });rn}
Die Methode consumeEmails stellt eine Verbindung zu Ihrer Warteschlange her, durchsucht diese, ruft die Details der einzelnen Einträge ab und leitet sie an sendEmail weiter. Sobald die E-Mail erfolgreich versendet wurde, wird sie im Register der Warteschlange als verarbeitet markiert.
Schauen wir uns die Methode sendEmail genauer an:
public void sendEmail(String recipient, String subject, String body) {rn // Create async clientrn ExecutorService executor = Executors.newFixedThreadPool(1);rn AsyncClient.Default<Object> asyncClient = new AsyncClient.Default<>(rn new Client.Default(null, null), executor);rnrn MailgunMessagesApi mailgunMessagesApi = MailgunClient.config("<SINCH_MAILGUN_API_KEY>")rn .client(asyncClient)rn .createApi(MailgunMessagesApi.class);rnrn Message message = Message.builder()rn .from("<REPLY_INBOX>")rn .to(recipient)rn .subject(subject)rn .text(body)rn .build();rnrn CompletableFuture<MessageResponse> messageResponse = mailgunMessagesApi.sendMessageAsync("<SINCH_MAILGUN_DOMAIN>", message);rn // Log message responsern System.out.println(messageResponse);rnrn}
Wenn Sie die Standard- Sendemethode (mailgunMessagesApi.sendMessage("", message)) verwenden, legt Mailgun Ihre Nachricht zur Verarbeitung in eine Warteschlange. Sie umgehen die Warteschlange, indem Sie die Nachricht asynchron senden. Hierfür erstellen Sie einen AsyncClient, um eine Bindung zur Mailgun Messages API herzustellen.
Anschließend erstellt der Code Ihre Nachricht mit den Details aus der RabbitMQ-Warteschlange und versendet sie über die Mailgun Messages API.
Um Ihre Anwendung auszuführen, fügen Sie einen Aufruf der Methode startProcessing() zu Ihrer main-Methode sowie etwas Code zur Handhabung der Anwendungsbeendigung hinzu:
public static void main(String[] args) { rn // Your main class rn SinchRabbitMQConsumer processor = new SinchRabbitMQConsumer(); rn processor.startProcessing();rnrn // Add a shutdown hook to stop processing when the application is terminatedrn Runtime.getRuntime().addShutdownHook(new Thread(processor::stopProcessing));rn }
Fazit
In diesem Artikel haben Sie gelernt, wie Warteschlangen dabei helfen, den Ablauf Ihrer Massen-E-Mail-Kampagnen zu verwalten, und Ihnen eine detaillierte Kontrolle über den Versand Ihrer Massen-E-Mails ermöglichen. Bei Implementierung über ein kompetentes Warteschlangenmanagement-System helfen Ihnen Warteschlangen, Probleme wie Ratenbegrenzung, Drosselung und Netzwerküberlastung zu mindern.
Unabhängig davon, wie Sie Ihre Warteschlangen implementieren und verwalten: Ein zuverlässiger Versandpartner wie Mailgun hilft Ihnen dabei.
Als API-First-Plattform bietet Mailgun Ihnen eine Vielzahl von Tools, mit denen Sie Ihre Kampagnen nicht nur versenden, sondern auch programmatisch skalieren, überwachen und tracken. Lesen Sie die offizielle Send API-Dokumentation und abonnieren Sie unbedingt unseren Newsletter für weitere Tutorials wie dieses.