Costruzione di uno strumento di convalida per destinatari di uccelli in blocco e in modo asincrono
Zachary Samuels
26 mag 2022
1 min read

Conclusioni principali
L'autore ha creato uno strumento di convalida dei destinatari in blocco per convalidare milioni di indirizzi email in modo efficiente utilizzando il Bird’s Recipient Validation API.
Node.js si è dimostrato più veloce e scalabile di Python grazie al suo I/O non bloccante e alla mancanza di limitazioni del GIL.
Lo strumento legge i file CSV in modo asincrono, chiama l'API di convalida per ogni email e scrive i risultati in un nuovo CSV in tempo reale.
L'approccio evita i colli di bottiglia della memoria e migliora il throughput a circa 100.000 convalide in meno di un minuto.
I miglioramenti futuri potrebbero includere una gestione dei tentativi più efficace, un'interfaccia utente più intuitiva o la migrazione in ambienti serverless per la scalabilità.
Q&A Highlights
Qual è lo scopo del Bulk Asynchronous Recipient Validation Tool?
Valida grandi volumi di indirizzi email integrandosi direttamente con l'API di convalida dei destinatari di Bird, fornendo rapidamente risultati verificati senza caricamenti manuali.
Perché Python è stato inizialmente utilizzato e poi sostituito da Node.js?
Il Global Interpreter Lock (GIL) di Python limitava la concorrenza, mentre Node.js permetteva una vera esecuzione asincrona, risultando in chiamate API parallele molto più veloci.
Come gestisce lo strumento i file di grandi dimensioni senza esaurire la memoria?
Invece di caricare tutti i dati in una volta sola, lo script elabora ogni riga CSV singolarmente, inviando la richiesta di convalida e scrivendo immediatamente i risultati in un nuovo file CSV.
Quale problema risolve lo strumento per gli sviluppatori?
Consente la convalida delle liste di email su larga scala, superando il limite di 20 MB del validatore basato su interfaccia utente di SparkPost ed eliminando la necessità di caricare manualmente più file.
Quanto è veloce la versione finale del programma?
Circa 100.000 convalide completate in 55 secondi, rispetto a oltre un minuto utilizzando la versione UI.
Quali problemi sono stati riscontrati sui sistemi Windows?
Il pooling delle connessioni del client HTTP di Node.js ha causato errori "ENOBUFS" dopo molte richieste simultanee, che sono stati risolti configurando il riutilizzo della connessione axios.
Quali miglioramenti futuri sono suggeriti?
Aggiunta di gestione degli errori e tentativi, creazione di un'interfaccia front-end o implementazione dello strumento come una funzione serverless di Azure per una migliore scalabilità e resilienza.
Per chi cerca un programma semplice e veloce che prenda un CSV, chiami l'API di convalida del destinatario e produca un CSV, questo programma fa per te.
Quando si costruiscono applicazioni email, gli sviluppatori spesso devono integrare più servizi e API. Comprendere i fondamenti dell'email API nell'infrastruttura cloud fornisce la base per costruire strumenti robusti come il sistema di validazione in massa che creeremo in questa guida.
Una delle domande che riceviamo occasionalmente è, come posso validare in massa liste di email con la validazione dei destinatari? Ci sono due opzioni qui, una è caricare un file tramite l'interfaccia utente SparkPost per la validazione, e l'altra è effettuare chiamate individuali per email all'API (dato che l'API è per la validazione di singole email).
La prima opzione funziona alla grande ma ha un limite di 20 Mb (circa 500.000 indirizzi). E se qualcuno avesse una lista di email contenente milioni di indirizzi? Potrebbe significare suddividere il tutto in migliaia di caricamenti di file CSV.
Poiché caricare migliaia di file CSV sembra un po' inverosimile, ho preso questo caso d'uso e ho cominciato a chiedermi quanto velocemente potessi far funzionare l'API. In questo post del blog, spiegherò cosa ho provato e come sono arrivato infine a un programma che potrebbe ottenere circa 100.000 validazioni in 55 secondi (mentre nell'interfaccia utente ho ottenuto circa 100.000 validazioni in 1 minuto e 10 secondi). E mentre questo richiederebbe ancora circa 100 ore per completare circa 654 milioni di validazioni, questo script può essere eseguito in background risparmiando tempo considerevole.
La versione finale di questo programma può essere trovata qui.
Il mio primo errore: usare Python
Python è uno dei miei linguaggi di programmazione preferiti. Eccelle in molte aree ed è incredibilmente semplice. Tuttavia, un'area in cui non eccelle è nei processi concorrenti. Sebbene Python abbia la capacità di eseguire funzioni asincrone, ha ciò che è noto come The Python Global Interpreter Lock o GIL.
“The Python Global Interpreter Lock o GIL, in parole semplici, è un mutex (o un blocco) che consente a un solo thread di tenere il controllo dell'interprete Python.
Ciò significa che solo un thread può essere in uno stato di esecuzione in qualsiasi momento. L'impatto del GIL non è visibile agli sviluppatori che eseguono programmi single-threaded, ma può essere un collo di bottiglia delle prestazioni nel codice vincolato dalla CPU e multi-thread.
Poiché il Global Interpreter Lock (GIL) consente a un solo thread di essere eseguito alla volta, anche su sistemi multi-core, ha guadagnato una reputazione come una caratteristica “infame” di Python (vedi l'articolo di Real Python sul GIL).
All'inizio non ero a conoscenza del GIL, quindi ho iniziato a programmare in Python. Alla fine, anche se il mio programma era asincrono, si bloccava, e indipendentemente da quanti thread aggiungevo, ottenevo comunque solo circa 12-15 iterazioni al secondo.
La parte principale della funzione asincrona in Python può essere vista qui sotto:
Così ho abbandonato l'uso di Python e sono tornato al tavolo da disegno...
Ho deciso di utilizzare NodeJS per la sua capacità di eseguire operazioni di I/O non bloccanti estremamente bene. Un'altra eccellente opzione per gestire l'elaborazione asincrona delle API è la costruzione di consumatori di webhook serverless con Azure Functions, che possono gestire efficacemente carichi di lavoro variabili. Inoltre, sono piuttosto familiare con la programmazione in NodeJS.
Utilizzando aspetti asincroni di Node.js, questo approccio ha funzionato bene. Per ulteriori dettagli sulla programmazione asincrona in Node.js, vedi la guida di RisingStack alla programmazione asincrona in Node.js.
Il mio secondo errore: cercare di leggere il file nella memoria
Scomposizione del codice finale
Dopo aver letto e validato gli argomenti del terminale, eseguo il seguente codice. Innanzitutto, leggo il file CSV delle email e conto ogni riga. Ci sono due scopi per questa funzione: 1) mi permette di riportare accuratamente il progresso del file [come vedremo più avanti], e 2) mi permette di fermare un timer quando il numero di email nel file è uguale alle validazioni completate. Ho aggiunto un timer in modo da poter eseguire benchmark e garantire buoni risultati.
Chiamo quindi la funzione validateRecipients. Nota che questa funzione è asincrona. Dopo aver validato che infile e outfile siano CSV, scrivo una riga di intestazione e avvio un timer del programma utilizzando la libreria JSDOM.
Lo script seguente è davvero la parte principale del programma, quindi lo dividerò e spiegherò cosa sta accadendo. Per ogni riga di infile:
Prendere asincronamente quella riga e chiamare l' recipient validation API.
Quindi, alla risposta
Aggiungere l'email al JSON (per poter stampare l'email nel CSV)
Validare se il motivo è nullo e, in tal caso, popolare un valore vuoto (questo è per mantenere il formato CSV coerente, poiché in alcuni casi il motivo è fornito nella risposta)
Impostare le opzioni e le chiavi per il modulo json2csv.
Convertire il JSON in CSV e eseguire l'output (utilizzando json2csv)
Scrivi il progresso nel terminale
Infine, se il numero di email nel file = validazioni completate, fermare il timer e stampare i risultati
Un ultimo problema che ho trovato è che mentre questo funzionava bene su Mac, ho riscontrato il seguente errore utilizzando Windows dopo circa 10.000 validazioni:
Errore: connect ENOBUFS XX.XX.XXX.XXX:443 – Local (undefined:undefined) con email XXXXXXX@XXXXXXXXXX.XXX
Dopo aver effettuato ulteriori ricerche, sembra essere un problema con il pool di connessioni del client HTTP NodeJS che non riutilizza le connessioni. Ho trovato questo Stackoverflow article sul problema e, dopo ulteriori scavi, ho trovato una buona configurazione di default per la libreria axios che ha risolto questo problema. Non sono ancora certo del motivo per cui questo problema si verifica solo su Windows e non su Mac.
Passi successivi
Per chi cerca un programma semplice e veloce che accetta un CSV, chiama l'API di validazione del destinatario e produce un CSV, questo programma fa per te.
Alcune aggiunte a questo programma potrebbero essere le seguenti:
Costruire un front-end o un'interfaccia utente più semplice da usare
Migliorare la gestione degli errori e dei tentativi perché se per qualche motivo l'API genera un errore, attualmente il programma non ritenta la chiamata
Considera l'implementazione come una funzione serverless di Azure per scalabilità automatica e gestione ridotta dell'infrastruttura
Sarei anche curioso di vedere se risultati più rapidi potrebbero essere ottenuti con un altro linguaggio come Golang o Erlang/Elixir. Oltre alla scelta del linguaggio, le limitazioni dell'infrastruttura possono anche influire sulle prestazioni - lo abbiamo appreso in prima persona quando abbiamo incontrato i limiti DNS non documentati in AWS che hanno influenzato i nostri sistemi di elaborazione email ad alto volume.
Per gli sviluppatori interessati a combinare l'elaborazione delle API con strumenti di flusso di lavoro visivi, scopri come integrare Flow Builder con Google Cloud Functions per flussi di lavoro di automazione senza codice.
Non esitate a fornirmi qualsiasi feedback o suggerimento per ampliare questo progetto.





