Reach

Grow

Manage

Automate

Reach

Grow

Manage

Automate

Membangun Alat Validasi Penerima Burung Asinkron Massal

Email

1 min read

Membangun Alat Validasi Penerima Burung Asinkron Massal

Email

1 min read

Membangun Alat Validasi Penerima Burung Asinkron Massal

Untuk seseorang yang sedang mencari program sederhana dan cepat yang menerima file csv, memanggil API validasi penerima, dan mengeluarkan file CSV, program ini untuk Anda.

Salah satu pertanyaan yang kadang-kadang kami terima adalah, bagaimana saya dapat memvalidasi daftar email secara massal dengan validasi penerima? Ada dua pilihan di sini, satu adalah mengunggah file melalui antarmuka pengguna SparkPost untuk validasi, dan yang lainnya adalah melakukan panggilan individu per email ke API (karena API adalah validasi email tunggal).

Pilihan pertama bekerja dengan baik tetapi memiliki batasan 20Mb (sekitar 500.000 alamat). Bagaimana jika seseorang memiliki daftar email yang berisi jutaan alamat? Itu bisa berarti membagi menjadi 1.000’s unggahan file CSV.

Karena mengunggah ribuan file CSV tampak sedikit berlebihan, saya mengambil kasus penggunaan itu dan mulai bertanya-tanya seberapa cepat saya bisa menjalankan API. Dalam posting blog ini, saya akan menjelaskan apa yang saya coba dan bagaimana akhirnya saya menemukan program yang dapat melakukan sekitar 100.000 validasi dalam 55 detik (Sedangkan di antarmuka pengguna saya mendapatkan sekitar 100.000 validasi dalam 1 menit 10 detik). Dan meskipun ini masih akan memakan waktu sekitar 100 jam untuk menyelesaikan sekitar 654 juta validasi, skrip ini dapat berjalan di latar belakang menghemat waktu yang signifikan.

Versi akhir dari program ini dapat ditemukan di sini.

Kesalahan pertama saya: menggunakan Python

Python adalah salah satu bahasa pemrograman favorit saya. Python menonjol di banyak bidang dan sangat mudah dipahami. Namun, salah satu area yang tidak unggul adalah proses bersamaan. Meskipun Python memiliki kemampuan menjalankan fungsi asinkron, ia memiliki apa yang disebut dengan Kunci Interpretor Global Python atau GIL.

"Kunci Interpretor Global Python atau GIL, secara sederhana, adalah mutex (atau kunci) yang memungkinkan hanya satu thread untuk memegang kendali interpreter Python.

Ini berarti bahwa hanya satu thread yang dapat berada dalam keadaan eksekusi pada waktu tertentu. Dampak GIL tidak terlihat oleh pengembang yang menjalankan program single-threaded, tetapi dapat menjadi penghambat kinerja dalam kode yang mengandalkan CPU dan multi-threaded.

Karena GIL hanya memungkinkan satu thread untuk dieksekusi pada satu waktu bahkan dalam arsitektur multi-threaded dengan lebih dari satu inti CPU, GIL telah mendapatkan reputasi sebagai fitur "terkenal" dari Python." (https://realpython.com/python-gil/)

Pada awalnya, saya tidak menyadari keberadaan GIL, jadi saya mulai memprogram menggunakan Python. Pada akhirnya, meskipun program saya asinkron, program tersebut tetap terkunci, dan tidak peduli berapa banyak thread yang saya tambahkan, saya tetap hanya mendapatkan sekitar 12-15 iterasi per detik.

Bagian utama dari fungsi asinkron di Python dapat dilihat di bawah ini:

async def validateRecipients(f, fh, apiKey, snooze, count): h = {'Authorization': apiKey, 'Accept': 'application/json'} with tqdm(total=count) as pbar: async with aiohttp.ClientSession() as session: for address in f: for i in address: thisReq = requests.compat.urljoin(url, i) async with session.get(thisReq,headers=h, ssl=False) as resp: content = await resp.json() row = content['results'] row['email'] = i fh.writerow(row) pbar.update(1)

Jadi saya mengabaikan penggunaan Python dan kembali ke papan gambar…

Saya memilih untuk menggunakan NodeJS karena kemampuannya melakukan operasi i/o non-blokir dengan sangat baik. Saya juga cukup familiar dengan pemrograman di NodeJS.

Menggunakan aspek asinkron dari NodeJS, ini akhirnya bekerja dengan baik. Untuk lebih jelasnya tentang pemrograman asinkron di NodeJS, lihat https://blog.risingstack.com/node-hero-async-programming-in-node-js/

Kesalahan kedua saya: mencoba membaca file ke dalam memori

Ide awal saya adalah sebagai berikut:

Flowchart illustrating the process of validating a CSV list of emails, starting with ingestion, format checking, asynchronous API validation, result aggregation, and concluding with outputting to a CSV file.


Pertama, masukkan daftar email CSV. Kedua, muat email ke dalam array dan pastikan bahwa mereka dalam format yang benar. Ketiga, panggil API validasi penerima secara asinkron. Keempat, tunggu hasilnya dan muat ke dalam variabel. Dan akhirnya, keluarkan variabel ini ke file CSV.

Ini bekerja dengan sangat baik untuk file yang lebih kecil. Masalahnya muncul ketika saya mencoba menjalankan 100.000 email. Program terhenti sekitar 12.000 validasi. Dengan bantuan salah satu pengembang front-end kami, saya melihat bahwa masalahnya adalah dengan memuat semua hasil ke dalam variabel (dan karena itu kehabisan memori dengan cepat). Jika Anda ingin melihat iterasi pertama program ini, saya telah menautkannya di sini: Version 1 (NOT RECOMMENDED).


Flowchart illustrating an email processing workflow, showing steps from ingesting a CSV list of emails to outputting results to a CSV file, with asynchronous validation via an API.


Pertama, masukkan daftar email CSV. Kedua, hitung jumlah email dalam file untuk tujuan pelaporan. Ketiga, saat setiap baris dibaca secara asinkron, panggil API validasi penerima dan keluarkan hasilnya ke file CSV.

Jadi, untuk setiap baris yang dibaca, saya memanggil API dan menuliskan hasilnya secara asinkron sehingga tidak menyimpan data ini dalam memori jangka panjang. Saya juga menghapus pengecekan sintaks email setelah berbicara dengan tim validasi penerima, karena mereka memberi tahu saya bahwa validasi penerima sudah memiliki pengecekan bawaan untuk memeriksa apakah email valid atau tidak.

Ide awal saya adalah sebagai berikut:

Flowchart illustrating the process of validating a CSV list of emails, starting with ingestion, format checking, asynchronous API validation, result aggregation, and concluding with outputting to a CSV file.


Pertama, masukkan daftar email CSV. Kedua, muat email ke dalam array dan pastikan bahwa mereka dalam format yang benar. Ketiga, panggil API validasi penerima secara asinkron. Keempat, tunggu hasilnya dan muat ke dalam variabel. Dan akhirnya, keluarkan variabel ini ke file CSV.

Ini bekerja dengan sangat baik untuk file yang lebih kecil. Masalahnya muncul ketika saya mencoba menjalankan 100.000 email. Program terhenti sekitar 12.000 validasi. Dengan bantuan salah satu pengembang front-end kami, saya melihat bahwa masalahnya adalah dengan memuat semua hasil ke dalam variabel (dan karena itu kehabisan memori dengan cepat). Jika Anda ingin melihat iterasi pertama program ini, saya telah menautkannya di sini: Version 1 (NOT RECOMMENDED).


Flowchart illustrating an email processing workflow, showing steps from ingesting a CSV list of emails to outputting results to a CSV file, with asynchronous validation via an API.


Pertama, masukkan daftar email CSV. Kedua, hitung jumlah email dalam file untuk tujuan pelaporan. Ketiga, saat setiap baris dibaca secara asinkron, panggil API validasi penerima dan keluarkan hasilnya ke file CSV.

Jadi, untuk setiap baris yang dibaca, saya memanggil API dan menuliskan hasilnya secara asinkron sehingga tidak menyimpan data ini dalam memori jangka panjang. Saya juga menghapus pengecekan sintaks email setelah berbicara dengan tim validasi penerima, karena mereka memberi tahu saya bahwa validasi penerima sudah memiliki pengecekan bawaan untuk memeriksa apakah email valid atau tidak.

Ide awal saya adalah sebagai berikut:

Flowchart illustrating the process of validating a CSV list of emails, starting with ingestion, format checking, asynchronous API validation, result aggregation, and concluding with outputting to a CSV file.


Pertama, masukkan daftar email CSV. Kedua, muat email ke dalam array dan pastikan bahwa mereka dalam format yang benar. Ketiga, panggil API validasi penerima secara asinkron. Keempat, tunggu hasilnya dan muat ke dalam variabel. Dan akhirnya, keluarkan variabel ini ke file CSV.

Ini bekerja dengan sangat baik untuk file yang lebih kecil. Masalahnya muncul ketika saya mencoba menjalankan 100.000 email. Program terhenti sekitar 12.000 validasi. Dengan bantuan salah satu pengembang front-end kami, saya melihat bahwa masalahnya adalah dengan memuat semua hasil ke dalam variabel (dan karena itu kehabisan memori dengan cepat). Jika Anda ingin melihat iterasi pertama program ini, saya telah menautkannya di sini: Version 1 (NOT RECOMMENDED).


Flowchart illustrating an email processing workflow, showing steps from ingesting a CSV list of emails to outputting results to a CSV file, with asynchronous validation via an API.


Pertama, masukkan daftar email CSV. Kedua, hitung jumlah email dalam file untuk tujuan pelaporan. Ketiga, saat setiap baris dibaca secara asinkron, panggil API validasi penerima dan keluarkan hasilnya ke file CSV.

Jadi, untuk setiap baris yang dibaca, saya memanggil API dan menuliskan hasilnya secara asinkron sehingga tidak menyimpan data ini dalam memori jangka panjang. Saya juga menghapus pengecekan sintaks email setelah berbicara dengan tim validasi penerima, karena mereka memberi tahu saya bahwa validasi penerima sudah memiliki pengecekan bawaan untuk memeriksa apakah email valid atau tidak.

Memecah kode terakhir

Setelah membaca dan memvalidasi argumen terminal, saya menjalankan kode berikut. Pertama, saya membaca file CSV email dan menghitung setiap baris. Ada dua tujuan dari fungsi ini, 1) ini memungkinkan saya untuk secara akurat melaporkan kemajuan file [seperti yang akan kita lihat nanti], dan 2) ini memungkinkan saya untuk menghentikan timer ketika jumlah email dalam file sama dengan validasi yang telah selesai. Saya menambahkan timer agar saya bisa menjalankan tolok ukur dan memastikan saya mendapatkan hasil yang baik.

let count = 0; //Penghitungan baris require("fs") .createReadStream(myArgs[1]) .on("data", function (chunk) { for (let i = 0; i < chunk.length; ++i) if (chunk[i] == 10) count++; }) //Membaca infile dan menambah jumlah untuk setiap baris .on("close", function () { //Pada akhir infile, setelah semua baris dihitung, jalankan fungsi validasi penerima validateRecipients.validateRecipients(count, myArgs); });


 Kemudian saya memanggil fungsi validateRecipients. Catat bahwa fungsi ini asinkron. Setelah memvalidasi bahwa infile dan outfile adalah CSV, saya menulis baris header, dan memulai timer program menggunakan perpustakaan JSDOM.

async function validateRecipients(email_count, myArgs) { if ( //Jika baik infile dan outfile dalam format .csv extname(myArgs[1]).toLowerCase() == ".csv" && extname(myArgs[3]).toLowerCase() == ".csv" ) { let completed = 0; //Penghitung untuk setiap panggilan API email_count++; //Penghitung baris mengembalikan #baris - 1, ini dilakukan untuk memperbaiki jumlah baris //Memulai timer const { window } = new JSDOM(); const start = window.performance.now(); const output = fs.createWriteStream(myArgs[3]); //Outfile output.write( "Email,Valid,Result,Reason,Is_Role,Is_Disposable,Is_Free,Delivery_Confidence\n" ); //Menulis header dalam outfile


Skrip berikut ini adalah inti dari program sehingga saya akan memecahnya dan menjelaskan apa yang terjadi. Untuk setiap baris dari infile:

fs.createReadStream(myArgs[1]) .pipe(csv.parse({ headers: false })) .on("data", async (email) => { let url = SPARKPOST_HOST + "/api/v1/recipient-validation/single/" + email; await axios .get(url, { headers: { Authorization: SPARKPOST_API_KEY, }, }) //Untuk setiap baris yang dibaca dari infile, memanggil SparkPost Recipient Validation API


Kemudian, pada responsnya:

  • Tambahkan email ke JSON (agar dapat mencetak email dalam CSV)

  • Validasi jika alasan (reason) adalah null, dan jika iya, isi dengan nilai kosong (ini agar format CSV konsisten, karena dalam beberapa kasus alasan diberikan dalam respons)

  • Tentukan opsi dan kunci untuk modul json2csv.

  • Konversi JSON ke CSV dan keluaran (menggunakan json2csv)

  • Tulis kemajuan di terminal

  • Akhirnya, jika jumlah email dalam file = validasi yang selesai, hentikan timer dan cetak hasilnya


.then(function (response) { response.data.results.email = String(email); //Menambahkan email sebagai nilai/pasangan kunci ke JSON respons untuk digunakan dalam keluaran response.data.results.reason ? null : (response.data.results.reason = ""); //Jika alasan adalah null, atur ke kosong sehingga CSV uniform //Menggunakan json-2-csv untuk mengubah JSON menjadi format CSV dan keluaran let options = { prependHeader: false, //Menonaktifkan nilai JSON ditambah sebagai baris header setiap baris keys: [ "results.email", "results.valid", "results.result", "results.reason", "results.is_role", "results.is_disposable", "results.is_free", "results.delivery_confidence", ], //Mengatur urutan kunci }; let json2csvCallback = function (err, csv) { if (err) throw err; output.write(`${csv}\n`); }; converter.json2csv(response.data, json2csvCallback, options); completed++; //Meningkatkan penghitung API process.stdout.write(`Selesai dengan ${completed} / ${email_count}\r`); //Mengeluarkan status Selesai / Total ke konsol tanpa menampilkan baris baru //Jika semua email telah selesai divalidasi if (completed == email_count) { const stop = window.performance.now(); //Menghentikan timer console.log( `Semua email berhasil divalidasi dalam ${ (stop - start) / 1000 } detik` ); } })

 

Satu masalah terakhir yang saya temukan adalah saat ini bekerja dengan baik di Mac, saya mengalami kesalahan berikut menggunakan Windows setelah sekitar 10,000 validasi:

Error: connect ENOBUFS XX.XX.XXX.XXX:443 – Lokal (undefined:undefined) dengan email XXXXXXX@XXXXXXXXXX.XXX

Setelah melakukan beberapa penelitian lebih lanjut, tampaknya ini adalah masalah dengan pool koneksi klien HTTP NodeJS yang tidak memanfaatkan koneksi yang sudah ada. Saya menemukan artikel Stackoverflow tentang masalah ini, dan setelah menggali lebih dalam, menemukan konfigurasi default yang bagus untuk pustaka axios yang menyelesaikan masalah ini. Saya masih belum yakin mengapa isu ini hanya terjadi di Windows dan tidak di Mac.

Langkah Selanjutnya

Untuk seseorang yang mencari program cepat dan sederhana yang menerima csv, memanggil recipient validation API, dan mengeluarkan CSV, program ini adalah untuk Anda.

Beberapa tambahan untuk program ini adalah sebagai berikut:

  • Membangun front end atau UI yang lebih mudah untuk digunakan

  • Penanganan kesalahan dan pengulangan yang lebih baik karena jika karena suatu alasan API mengeluarkan kesalahan, program saat ini tidak mengulangi panggilan


Saya juga penasaran apakah hasil yang lebih cepat dapat dicapai dengan bahasa lain seperti Golang atau Erlang/Elixir.

Silakan merasa bebas untuk memberikan umpan balik atau saran kepada saya untuk memperluas proyek ini.

Bergabunglah dengan Newsletter kami.

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

Bergabunglah dengan Newsletter kami.

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

Bergabunglah dengan Newsletter kami.

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

Logo Pinterest
Uber logo
Square logo
Logo Adobe
Logo Meta
Logo PayPal

Perusahaan

Newsletter

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

Uber logo
Square logo
Logo Adobe
Logo Meta

Perusahaan

Newsletter

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

Uber logo
Logo Adobe
Logo Meta

Reach

Grow

Manage

Automate

Sumber Daya

Perusahaan

Newsletter

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.