Entwicklung eines Bulk-Asynchronen Vogel-Empfänger-Validierungstools
Zachary Samuels
26.05.2022
1 min read

Wichtige Erkenntnisse
Der Autor hat ein Tool zur Validierung von Massenempfängern entwickelt, um Millionen von E-Mail-Adressen effizient mit der Recipient Validation API von Bird zu validieren.
Node.js erwies sich als schneller und skalierbarer als Python aufgrund seines nicht blockierenden I/O und der fehlenden GIL-Beschränkungen.
Das Tool liest CSV-Dateien asynchron ein, ruft die Validation API für jede E-Mail auf und schreibt die Ergebnisse in Echtzeit in eine neue CSV.
Der Ansatz vermeidet Speicherengpässe und verbessert den Durchsatz auf etwa 100.000 Validierungen in weniger als einer Minute.
Zukünftige Verbesserungen könnten eine bessere Wiederholungsverwaltung, eine benutzerfreundliche Benutzeroberfläche oder die Migration zu serverlosen Umgebungen zur Skalierbarkeit umfassen.
Q&A Highlights
Was ist der Zweck des Bulk Asynchronous Recipient Validation Tool?
Es überprüft große Mengen von E-Mail-Adressen, indem es direkt mit Bird’s Recipient Validation API integriert wird und schnell verifizierte Ergebnisse ausgibt, ohne manuelle Uploads.
Warum wurde Python zunächst verwendet und später durch Node.js ersetzt?
Der Global Interpreter Lock (GIL) von Python begrenzte die Parallelität, während Node.js echte asynchrone Ausführung ermöglichte, was zu deutlich schnelleren parallelen API-Aufrufen führte.
Wie verarbeitet das Tool große Dateien, ohne den Speicher zu überlasten?
Statt alle Daten auf einmal zu laden, verarbeitet das Skript jede CSV-Zeile einzeln—sendet die Validierungsanfrage und schreibt die Ergebnisse sofort in eine neue CSV-Datei.
Welches Problem löst das Tool für Entwickler?
Es ermöglicht die Validierung von E-Mail-Listen in großem Maßstab, wobei die 20MB-Grenze des SparkPost’s UI-basierten Validators überschritten und die Notwendigkeit, mehrere Dateien manuell hochzuladen, beseitigt wird.
Wie schnell ist die endgültige Version des Programms?
Rund 100.000 Validierungen abgeschlossen in 55 Sekunden, verglichen mit über einer Minute bei Verwendung der UI-Version.
Welche Probleme traten auf Windows-Systemen auf?
Node.js HTTP-Client-Verbindungs-Pooling verursachte „ENOBUFS“-Fehler nach vielen gleichzeitigen Anfragen, die durch das Konfigurieren der Axios-Verbindungswiederverwendung behoben wurden.
Welche zukünftigen Verbesserungen werden vorgeschlagen?
Fehlerbehandlung und Wiederholungen hinzufügen, eine Front-End-Schnittstelle erstellen oder das Tool als serverlose Azure-Funktion implementieren, um bessere Skalierbarkeit und Belastbarkeit zu erreichen.
Für jemanden, der nach einem einfachen, schnellen Programm sucht, das eine CSV-Datei einliest, die Empfänger-Validierungs-API aufruft und eine CSV-Datei ausgibt, ist dieses Programm genau das Richtige für Sie.
Beim Erstellen von E-Mail-Anwendungen müssen Entwickler oft mehrere Dienste und APIs integrieren. Das Verständnis von E-Mail-API-Grundlagen in der Cloud-Infrastruktur bietet die Grundlage für den Aufbau robuster Werkzeuge wie das Massenvalidierungssystem, das wir in diesem Leitfaden erstellen werden.
Eine der Fragen, die wir gelegentlich erhalten, ist: Wie kann ich E-Mail-Listen mit Empfängervalidierung in großen Mengen validieren? Es gibt hier zwei Möglichkeiten: Eine besteht darin, eine Datei über die SparkPost-Benutzeroberfläche zur Validierung hochzuladen, die andere besteht darin, einzelne Anfragen pro E-Mail an die API zu richten (da die API eine Einzel-E-Mail-Validierung ist).
Die erste Option funktioniert großartig, hat jedoch eine Begrenzung von 20 MB (etwa 500.000 Adressen). Was ist, wenn jemand eine E-Mail-Liste mit Millionen von Adressen hat? Es könnte bedeuten, dass man diese in Tausende von CSV-Datei-Uploads aufteilen muss.
Da das Hochladen von Tausenden von CSV-Dateien ein wenig weit hergeholt erscheint, habe ich diesen Anwendungsfall genommen und begann mich zu fragen, wie schnell ich die API zum Laufen bringen könnte. In diesem Blogbeitrag werde ich erklären, was ich ausprobiert habe und wie ich schließlich zu einem Programm gekommen bin, das ungefähr 100.000 Validierungen in 55 Sekunden durchführen konnte (während ich in der Benutzeroberfläche etwa 100.000 Validierungen in 1 Minute 10 Sekunden erreicht habe). Und obwohl es immer noch etwa 100 Stunden dauern würde, um etwa 654 Millionen Validierungen abzuschließen, kann dieses Skript im Hintergrund laufen und erheblich Zeit sparen.
Die endgültige Version dieses Programms finden Sie hier.
Mein erster Fehler: die Verwendung von Python
Python ist eine meiner Lieblingsprogrammiersprachen. Es glänzt in vielen Bereichen und ist unglaublich unkompliziert. In einem Bereich glänzt es jedoch nicht: bei parallelen Prozessen. Während Python die Fähigkeit hat, asynchrone Funktionen auszuführen, hat es das, was als der Python Global Interpreter Lock oder GIL bekannt ist.
„Der Python Global Interpreter Lock oder GIL, einfach ausgedrückt, ist ein Mutex (oder eine Sperre), die nur einem Thread erlaubt, die Kontrolle über den Python-Interpreter zu halten.
Das bedeutet, dass zu jedem Zeitpunkt nur ein Thread im Zustand der Ausführung sein kann. Die Auswirkung des GIL ist für Entwickler, die Programme mit einem einzigen Thread ausführen, nicht sichtbar, kann jedoch in CPU-intensivem und multi-threaded Codes eine Leistungsbremse darstellen.
Da der Global Interpreter Lock (GIL) nur einem Thread die Ausführung gleichzeitig erlaubt, selbst auf Multi-Core-Systemen, hat es den Ruf erlangt, ein „berüchtigtes“ Merkmal von Python zu sein (siehe Real Python’s Artikel über das GIL).
Zuerst war mir das GIL nicht bewusst, also begann ich mit dem Programmieren in Python. Am Ende, obwohl mein Programm asynchron war, wurde es blockiert, und egal wie viele Threads ich hinzufügte, ich erreichte trotzdem nur etwa 12-15 Iterationen pro Sekunde.
Der Hauptteil der asynchronen Funktion in Python ist unten zu sehen:
Also habe ich den Einsatz von Python verworfen und bin zurück ans Reißbrett…
Ich entschied mich dafür, NodeJS zu nutzen, da es nicht-blockierende I/O-Operationen extrem gut ausführen kann. Eine weitere ausgezeichnete Option zur Handhabung von asynchronem API-Processing ist der Bau von Serverless-Webhook-Konsumenten mit Azure Functions, die variable Arbeitslasten effizient verarbeiten können. Außerdem bin ich ziemlich vertraut mit dem Programmieren in NodeJS.
Unter Nutzung der asynchronen Aspekte von Node.js funktionierte dieser Ansatz gut. Für weitere Details über asynchrones Programmieren in Node.js, siehe RisingStack’s Leitfaden für asynchrones Programmieren in Node.js.
Mein zweiter Fehler: der Versuch, die Datei in den Speicher zu lesen
Aufschlüsselung des finalen Codes
Nach dem Einlesen und Überprüfen der Terminalargumente führe ich den folgenden Code aus. Zuerst lese ich die CSV-Datei der E-Mails ein und zähle jede Zeile. Diese Funktion hat zwei Zwecke: 1) Sie ermöglicht es mir, über den Fortschritt der Datei genau zu berichten [wie wir später sehen werden], und 2) es ermöglicht mir, einen Timer zu stoppen, wenn die Anzahl der E-Mails in der Datei der abgeschlossenen Validierungen entspricht. Ich habe einen Timer hinzugefügt, damit ich Benchmarks durchführen und sicherstellen kann, dass ich gute Ergebnisse erziele.
Dann rufe ich die Funktion validateRecipients auf. Beachten Sie, dass diese Funktion asynchron ist. Nachdem ich überprüft habe, dass die infile und outfile CSV-Dateien sind, schreibe ich eine Kopfzeile und starte einen Programm-Timer mit der JSDOM-Bibliothek.
Das folgende Skript ist wirklich der Hauptteil des Programms, daher werde ich es aufbrechen und erklären, was passiert. Für jede Zeile der infile:
Diese Zeile asynchron nehmen und die Empfänger-Validierungs-API aufrufen.
Dann, bei der Antwort
Die E-Mail zum JSON hinzufügen (um die E-Mail in der CSV ausgeben zu können)
Wenn der Grund null ist, prüfen, und falls ja, einen leeren Wert einfügen (dies dient dazu, das CSV-Format konsistent zu halten, da in einigen Fällen ein Grund in der Antwort angegeben wird)
Die Optionen und Schlüssel für das json2csv-Modul festlegen.
Das JSON in CSV konvertieren und ausgeben (mithilfe von json2csv)
Fortschritt im Terminal schreiben
Schließlich, wenn die Anzahl der E-Mails in der Datei = abgeschlossenen Validierungen ist, den Timer stoppen und die Ergebnisse drucken
Ein letztes Problem, das ich fand, war, dass dies auf Mac hervorragend funktionierte, ich jedoch nach etwa 10.000 Validierungen auf Windows auf den folgenden Fehler stieß:
Fehler: connect ENOBUFS XX.XX.XXX.XXX:443 – Lokale (undefined:undefined) mit E-Mail XXXXXXX@XXXXXXXXXX.XXX
Nach weitergehenden Recherchen scheint es ein Problem mit dem NodeJS-HTTP-Client-Verbindungspool zu sein, der Verbindungen nicht wiederverwendet. Ich fand diesen Stackoverflow-Artikel zu dem Problem und nach weiterer Recherche eine gute Standardkonfiguration für die axios-Bibliothek, die dieses Problem gelöst hat. Ich bin mir immer noch nicht sicher, warum dieses Problem nur unter Windows und nicht auf Mac auftritt.
Nächste Schritte
Für jemanden, der nach einem einfachen, schnellen Programm sucht, das eine CSV einliest, die Empfänger-Validation API aufruft und eine CSV ausgibt, ist dieses Programm genau das Richtige.
Einige Ergänzungen zu diesem Programm wären folgende:
Erstellen Sie eine Benutzeroberfläche oder ein Frontend für die einfachere Nutzung
Bessere Fehlerbehandlung und erneute Versuche, da das Programm derzeit den Aufruf nicht wiederholt, falls die API aus irgendeinem Grund einen Fehler ausgibt
Erwägen Sie die Implementierung als eine serverlose Azure-Funktion für automatisches Skalieren und reduzierte Infrastrukturverwaltung
Ich wäre auch neugierig zu sehen, ob schnellere Ergebnisse mit einer anderen Sprache wie Golang oder Erlang/Elixir erzielt werden könnten. Neben der Sprachwahl können auch Infrastrukturbeschränkungen die Leistung beeinflussen - das haben wir aus erster Hand erfahren, als wir auf undokumentierte DNS-Limits in AWS stießen, die unsere hochvolumigen E-Mail-Verarbeitungssysteme beeinträchtigten.
Für Entwickler, die daran interessiert sind, API-Verarbeitung mit visuellen Workflow-Tools zu kombinieren, sehen Sie sich an, wie Sie Flow Builder mit Google Cloud Functions integrieren können, um No-Code-Automationsworkflows zu realisieren.
Bitte zögern Sie nicht, mir Feedback oder Vorschläge zur Erweiterung dieses Projekts zu geben.





