Membangun Alat Validasi Penerima Burung Asinkron Massal
Zachary Samuels
26 Mei 2022
1 min read

Poin Penting
Penulis membangun alat validasi penerima massal untuk memvalidasi jutaan alamat email secara efisien menggunakan API Validasi Penerima Bird.
Node.js terbukti lebih cepat dan lebih scalable daripada Python karena non-blocking I/O dan tidak adanya batasan GIL.
Alat ini membaca file CSV secara asinkron, memanggil API validasi untuk setiap email, dan menulis hasilnya ke CSV baru secara real-time.
Pendekatan ini menghindari kemacetan memori dan meningkatkan throughput menjadi sekitar 100,000 validasi dalam waktu kurang dari satu menit.
Peningkatan di masa depan dapat mencakup penanganan ulang yang lebih baik, antarmuka pengguna yang ramah, atau migrasi ke lingkungan tanpa server untuk skalabilitas.
Sorotan Tanya jawab
Apa tujuan dari Alat Validasi Penerima Asinkron Massal?
Ini memvalidasi volume besar alamat email dengan mengintegrasikan langsung dengan API Validasi Penerima Bird, menghasilkan hasil yang terverifikasi dengan cepat tanpa perlu unggahan manual.
Mengapa Python awalnya digunakan dan kemudian digantikan oleh Node.js?
Kunci Interpreter Global Python (GIL) membatasi konkurensi, sementara Node.js memungkinkan eksekusi asinkron yang sebenarnya, menghasilkan panggilan API paralel yang jauh lebih cepat.
Bagaimana alat ini menangani file besar tanpa kehabisan memori?
Alih-alih memuat semua data sekaligus, skrip memproses setiap baris CSV secara individual—mengirimkan permintaan validasi dan segera menulis hasilnya ke file CSV baru.
Masalah apa yang dipecahkan oleh alat ini untuk pengembang?
Ini memungkinkan validasi daftar email dalam skala besar, mengatasi batas 20MB dari validator berbasis UI SparkPost dan menghilangkan kebutuhan untuk mengunggah banyak berkas secara manual.
Seberapa cepat versi final dari program tersebut?
Sekitar 100.000 validasi selesai dalam 55 detik, dibandingkan dengan lebih dari satu menit menggunakan versi UI.
Masalah apa yang ditemui di sistem Windows?
Pengelolaan koneksi klien HTTP Node.js menyebabkan kesalahan “ENOBUFS” setelah banyak permintaan bersamaan, yang diperbaiki dengan mengonfigurasi penggunaan ulang koneksi axios.
Apa peningkatan masa depan yang disarankan?
Menambahkan penanganan kesalahan dan pengulangan, membuat antarmuka front-end, atau menerapkan alat tersebut sebagai Fungsi Azure tanpa server untuk skalabilitas dan ketahanan yang lebih baik.
Bagi seseorang yang mencari program sederhana dan cepat yang menerima file csv, memanggil API validasi penerima, dan mengeluarkan file CSV, program ini cocok untuk Anda.
Ketika membangun aplikasi email, pengembang sering perlu mengintegrasikan beberapa layanan dan API. Memahami dasar-dasar API email dalam infrastruktur cloud memberikan fondasi untuk membangun alat yang tangguh seperti sistem validasi massal yang akan kita buat dalam panduan ini.
Salah satu pertanyaan yang kadang-kadang kami terima adalah, bagaimana saya bisa melakukan validasi massal daftar email dengan validasi penerima? Ada dua opsi di sini, satu adalah mengunggah file melalui UI SparkPost untuk validasi, dan yang lainnya adalah melakukan panggilan individu per email ke API (karena API hanya untuk validasi email tunggal).
Opsi 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-baginya menjadi 1.000-an unggahan file CSV.
Karena mengunggah ribuan file CSV tampaknya agak berlebihan, saya mengambil kasus penggunaan itu dan mulai bertanya-tanya seberapa cepat saya bisa mendapatkan API untuk berjalan. Dalam posting blog ini, saya akan menjelaskan apa yang saya coba dan bagaimana saya akhirnya datang ke program yang bisa mencapai 100.000 validasi dalam 55 detik (Sementara di UI saya mendapatkan sekitar 100.000 validasi dalam 1 menit 10 detik).
Pendekatan | Validasi Diuji | Waktu untuk Selesai | Perkiraan Throughput |
|---|---|---|---|
Tool Node.js async massal | 100.000 | 55 detik | ~1.818 validasi/detik |
Unggah UI SparkPost | 100.000 | 1 menit 10 detik | ~1.428 validasi/detik |
Dan sementara ini masih akan memakan waktu sekitar 100 jam untuk menyelesaikan dengan sekitar 654 juta validasi, skrip ini dapat berjalan di latar belakang menghemat waktu yang signifikan.
Versi akhir dari program ini dapat ditemukan di sini.
Ketika membangun aplikasi email, pengembang sering perlu mengintegrasikan beberapa layanan dan API. Memahami dasar-dasar API email dalam infrastruktur cloud memberikan fondasi untuk membangun alat yang tangguh seperti sistem validasi massal yang akan kita buat dalam panduan ini.
Salah satu pertanyaan yang kadang-kadang kami terima adalah, bagaimana saya bisa melakukan validasi massal daftar email dengan validasi penerima? Ada dua opsi di sini, satu adalah mengunggah file melalui UI SparkPost untuk validasi, dan yang lainnya adalah melakukan panggilan individu per email ke API (karena API hanya untuk validasi email tunggal).
Opsi 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-baginya menjadi 1.000-an unggahan file CSV.
Karena mengunggah ribuan file CSV tampaknya agak berlebihan, saya mengambil kasus penggunaan itu dan mulai bertanya-tanya seberapa cepat saya bisa mendapatkan API untuk berjalan. Dalam posting blog ini, saya akan menjelaskan apa yang saya coba dan bagaimana saya akhirnya datang ke program yang bisa mencapai 100.000 validasi dalam 55 detik (Sementara di UI saya mendapatkan sekitar 100.000 validasi dalam 1 menit 10 detik).
Pendekatan | Validasi Diuji | Waktu untuk Selesai | Perkiraan Throughput |
|---|---|---|---|
Tool Node.js async massal | 100.000 | 55 detik | ~1.818 validasi/detik |
Unggah UI SparkPost | 100.000 | 1 menit 10 detik | ~1.428 validasi/detik |
Dan sementara ini masih akan memakan waktu sekitar 100 jam untuk menyelesaikan dengan sekitar 654 juta validasi, skrip ini dapat berjalan di latar belakang menghemat waktu yang signifikan.
Versi akhir dari program ini dapat ditemukan di sini.
Ketika membangun aplikasi email, pengembang sering perlu mengintegrasikan beberapa layanan dan API. Memahami dasar-dasar API email dalam infrastruktur cloud memberikan fondasi untuk membangun alat yang tangguh seperti sistem validasi massal yang akan kita buat dalam panduan ini.
Salah satu pertanyaan yang kadang-kadang kami terima adalah, bagaimana saya bisa melakukan validasi massal daftar email dengan validasi penerima? Ada dua opsi di sini, satu adalah mengunggah file melalui UI SparkPost untuk validasi, dan yang lainnya adalah melakukan panggilan individu per email ke API (karena API hanya untuk validasi email tunggal).
Opsi 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-baginya menjadi 1.000-an unggahan file CSV.
Karena mengunggah ribuan file CSV tampaknya agak berlebihan, saya mengambil kasus penggunaan itu dan mulai bertanya-tanya seberapa cepat saya bisa mendapatkan API untuk berjalan. Dalam posting blog ini, saya akan menjelaskan apa yang saya coba dan bagaimana saya akhirnya datang ke program yang bisa mencapai 100.000 validasi dalam 55 detik (Sementara di UI saya mendapatkan sekitar 100.000 validasi dalam 1 menit 10 detik).
Pendekatan | Validasi Diuji | Waktu untuk Selesai | Perkiraan Throughput |
|---|---|---|---|
Tool Node.js async massal | 100.000 | 55 detik | ~1.818 validasi/detik |
Unggah UI SparkPost | 100.000 | 1 menit 10 detik | ~1.428 validasi/detik |
Dan sementara ini masih akan memakan waktu sekitar 100 jam untuk menyelesaikan dengan 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. Ini unggul di banyak bidang dan sangat mudah digunakan. Namun, satu bidang di mana ia tidak unggul adalah proses bersamaan. Meskipun Python memiliki kemampuan untuk menjalankan fungsi asinkron, ia memiliki apa yang dikenal sebagai Python Global Interpreter Lock atau GIL.
“Python Global Interpreter Lock atau GIL, dalam kata-kata sederhana, adalah mutex (atau kunci) yang hanya mengizinkan satu utas untuk memegang kontrol dari interpreter Python.
Ini berarti bahwa hanya satu utas yang dapat berada dalam keadaan eksekusi pada suatu saat. Dampak dari GIL tidak terlihat bagi pengembang yang menjalankan program dengan satu utas, tetapi itu bisa menjadi bottleneck kinerja di kode yang terikat CPU dan multi-utas.
Karena Global Interpreter Lock (GIL) hanya mengizinkan satu utas untuk dieksekusi dalam satu waktu, bahkan pada sistem multi-core, itu telah mendapatkan reputasi sebagai fitur “terkenal buruk” dari Python (lihat artikel Real Python tentang GIL).
Awalnya, saya tidak sadar akan GIL, jadi saya mulai memprogram dalam Python. Pada akhirnya, meskipun program saya adalah asinkron, itu terjebak, dan tidak peduli seberapa banyak utas yang saya tambahkan, saya masih hanya mendapatkan sekitar 12-15 iterasi per detik.
Bagian utama dari fungsi asinkron di Python dapat dilihat di bawah:
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 menghentikan penggunaan Python dan kembali ke papan gambar…
Saya memilih untuk memanfaatkan NodeJS karena kemampuannya untuk melakukan operasi i/o non-blokir dengan sangat baik. Pilihan yang sangat baik lainnya untuk menangani pemrosesan API asinkron adalah membangun konsumen webhook tanpa server dengan Azure Functions, yang dapat menangani beban kerja yang bervariasi dengan efisien. Saya juga cukup familiar dengan pemrograman di NodeJS.
Dengan memanfaatkan aspek asinkron dari Node.js, pendekatan ini bekerja dengan baik. Untuk lebih jelas tentang pemrograman asinkron di Node.js, lihat panduan RisingStack tentang pemrograman asinkron di Node.js.
Python adalah salah satu bahasa pemrograman favorit saya. Ini unggul di banyak bidang dan sangat mudah digunakan. Namun, satu bidang di mana ia tidak unggul adalah proses bersamaan. Meskipun Python memiliki kemampuan untuk menjalankan fungsi asinkron, ia memiliki apa yang dikenal sebagai Python Global Interpreter Lock atau GIL.
“Python Global Interpreter Lock atau GIL, dalam kata-kata sederhana, adalah mutex (atau kunci) yang hanya mengizinkan satu utas untuk memegang kontrol dari interpreter Python.
Ini berarti bahwa hanya satu utas yang dapat berada dalam keadaan eksekusi pada suatu saat. Dampak dari GIL tidak terlihat bagi pengembang yang menjalankan program dengan satu utas, tetapi itu bisa menjadi bottleneck kinerja di kode yang terikat CPU dan multi-utas.
Karena Global Interpreter Lock (GIL) hanya mengizinkan satu utas untuk dieksekusi dalam satu waktu, bahkan pada sistem multi-core, itu telah mendapatkan reputasi sebagai fitur “terkenal buruk” dari Python (lihat artikel Real Python tentang GIL).
Awalnya, saya tidak sadar akan GIL, jadi saya mulai memprogram dalam Python. Pada akhirnya, meskipun program saya adalah asinkron, itu terjebak, dan tidak peduli seberapa banyak utas yang saya tambahkan, saya masih hanya mendapatkan sekitar 12-15 iterasi per detik.
Bagian utama dari fungsi asinkron di Python dapat dilihat di bawah:
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 menghentikan penggunaan Python dan kembali ke papan gambar…
Saya memilih untuk memanfaatkan NodeJS karena kemampuannya untuk melakukan operasi i/o non-blokir dengan sangat baik. Pilihan yang sangat baik lainnya untuk menangani pemrosesan API asinkron adalah membangun konsumen webhook tanpa server dengan Azure Functions, yang dapat menangani beban kerja yang bervariasi dengan efisien. Saya juga cukup familiar dengan pemrograman di NodeJS.
Dengan memanfaatkan aspek asinkron dari Node.js, pendekatan ini bekerja dengan baik. Untuk lebih jelas tentang pemrograman asinkron di Node.js, lihat panduan RisingStack tentang pemrograman asinkron di Node.js.
Python adalah salah satu bahasa pemrograman favorit saya. Ini unggul di banyak bidang dan sangat mudah digunakan. Namun, satu bidang di mana ia tidak unggul adalah proses bersamaan. Meskipun Python memiliki kemampuan untuk menjalankan fungsi asinkron, ia memiliki apa yang dikenal sebagai Python Global Interpreter Lock atau GIL.
“Python Global Interpreter Lock atau GIL, dalam kata-kata sederhana, adalah mutex (atau kunci) yang hanya mengizinkan satu utas untuk memegang kontrol dari interpreter Python.
Ini berarti bahwa hanya satu utas yang dapat berada dalam keadaan eksekusi pada suatu saat. Dampak dari GIL tidak terlihat bagi pengembang yang menjalankan program dengan satu utas, tetapi itu bisa menjadi bottleneck kinerja di kode yang terikat CPU dan multi-utas.
Karena Global Interpreter Lock (GIL) hanya mengizinkan satu utas untuk dieksekusi dalam satu waktu, bahkan pada sistem multi-core, itu telah mendapatkan reputasi sebagai fitur “terkenal buruk” dari Python (lihat artikel Real Python tentang GIL).
Awalnya, saya tidak sadar akan GIL, jadi saya mulai memprogram dalam Python. Pada akhirnya, meskipun program saya adalah asinkron, itu terjebak, dan tidak peduli seberapa banyak utas yang saya tambahkan, saya masih hanya mendapatkan sekitar 12-15 iterasi per detik.
Bagian utama dari fungsi asinkron di Python dapat dilihat di bawah:
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 menghentikan penggunaan Python dan kembali ke papan gambar…
Saya memilih untuk memanfaatkan NodeJS karena kemampuannya untuk melakukan operasi i/o non-blokir dengan sangat baik. Pilihan yang sangat baik lainnya untuk menangani pemrosesan API asinkron adalah membangun konsumen webhook tanpa server dengan Azure Functions, yang dapat menangani beban kerja yang bervariasi dengan efisien. Saya juga cukup familiar dengan pemrograman di NodeJS.
Dengan memanfaatkan aspek asinkron dari Node.js, pendekatan ini bekerja dengan baik. Untuk lebih jelas tentang pemrograman asinkron di Node.js, lihat panduan RisingStack tentang pemrograman asinkron di Node.js.
Kesalahan kedua saya: mencoba membaca file ke dalam memori
Ide awal saya adalah sebagai berikut:

Pertama, ambil daftar email dalam format CSV. Kedua, masukkan email ke dalam array dan periksa apakah formatnya sudah benar. Ketiga, panggil API validasi penerima secara asinkron. Keempat, tunggu hasilnya dan masukkan ke dalam variabel. Dan terakhir, keluarkan variabel ini ke file CSV.
Ini sangat berhasil untuk file yang lebih kecil. Masalah muncul ketika saya mencoba menjalankan 100.000 email. Program terhenti pada sekitar 12.000 validasi. Dengan bantuan salah satu pengembang front-end kami, saya melihat bahwa masalahnya adalah memuat semua hasil ke dalam variabel (dan oleh karena itu cepat kehabisan memori). Jika Anda ingin melihat iterasi pertama dari program ini, saya telah menautkannya di sini: Versi 1 (TIDAK DISARANKAN).

Pertama, ambil daftar email dalam format 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.
Dengan demikian, untuk setiap baris yang dibaca, saya memanggil API dan menuliskan hasilnya secara asinkron agar tidak menyimpan data ini dalam memori jangka panjang. Saya juga menghapus pemeriksaan sintaks email setelah berbicara dengan tim validasi penerima, karena mereka memberi tahu saya bahwa validasi penerima sudah memiliki pemeriksaan bawaan untuk memeriksa apakah email valid atau tidak.
Ide awal saya adalah sebagai berikut:

Pertama, ambil daftar email dalam format CSV. Kedua, masukkan email ke dalam array dan periksa apakah formatnya sudah benar. Ketiga, panggil API validasi penerima secara asinkron. Keempat, tunggu hasilnya dan masukkan ke dalam variabel. Dan terakhir, keluarkan variabel ini ke file CSV.
Ini sangat berhasil untuk file yang lebih kecil. Masalah muncul ketika saya mencoba menjalankan 100.000 email. Program terhenti pada sekitar 12.000 validasi. Dengan bantuan salah satu pengembang front-end kami, saya melihat bahwa masalahnya adalah memuat semua hasil ke dalam variabel (dan oleh karena itu cepat kehabisan memori). Jika Anda ingin melihat iterasi pertama dari program ini, saya telah menautkannya di sini: Versi 1 (TIDAK DISARANKAN).

Pertama, ambil daftar email dalam format 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.
Dengan demikian, untuk setiap baris yang dibaca, saya memanggil API dan menuliskan hasilnya secara asinkron agar tidak menyimpan data ini dalam memori jangka panjang. Saya juga menghapus pemeriksaan sintaks email setelah berbicara dengan tim validasi penerima, karena mereka memberi tahu saya bahwa validasi penerima sudah memiliki pemeriksaan bawaan untuk memeriksa apakah email valid atau tidak.
Ide awal saya adalah sebagai berikut:

Pertama, ambil daftar email dalam format CSV. Kedua, masukkan email ke dalam array dan periksa apakah formatnya sudah benar. Ketiga, panggil API validasi penerima secara asinkron. Keempat, tunggu hasilnya dan masukkan ke dalam variabel. Dan terakhir, keluarkan variabel ini ke file CSV.
Ini sangat berhasil untuk file yang lebih kecil. Masalah muncul ketika saya mencoba menjalankan 100.000 email. Program terhenti pada sekitar 12.000 validasi. Dengan bantuan salah satu pengembang front-end kami, saya melihat bahwa masalahnya adalah memuat semua hasil ke dalam variabel (dan oleh karena itu cepat kehabisan memori). Jika Anda ingin melihat iterasi pertama dari program ini, saya telah menautkannya di sini: Versi 1 (TIDAK DISARANKAN).

Pertama, ambil daftar email dalam format 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.
Dengan demikian, untuk setiap baris yang dibaca, saya memanggil API dan menuliskan hasilnya secara asinkron agar tidak menyimpan data ini dalam memori jangka panjang. Saya juga menghapus pemeriksaan sintaks email setelah berbicara dengan tim validasi penerima, karena mereka memberi tahu saya bahwa validasi penerima sudah memiliki pemeriksaan bawaan untuk memeriksa apakah email valid atau tidak.
Membongkar kode akhir
Setelah membaca dan memvalidasi argumen terminal, saya menjalankan kode berikut. Pertama, saya membaca file CSV dari email dan menghitung setiap baris. Ada dua tujuan dari fungsi ini, 1) ini memungkinkan saya untuk melaporkan kemajuan file dengan akurat [seperti yang akan kita lihat nanti], dan 2) ini memungkinkan saya untuk menghentikan timer ketika jumlah email dalam file sama dengan validasi yang selesai. Saya menambahkan timer agar saya dapat menjalankan benchmark dan memastikan saya mendapatkan hasil yang baik.
let count = 0; // Line count require("fs") .createReadStream(myArgs[1]) .on("data", function (chunk) { for (let i = 0; i < chunk.length; ++i) if (chunk[i] == 10) count++; }) // Reads the infile and increases the count for each line .on("close", function () { // At the end of the infile, after all lines have been counted, run the recipient validation function validateRecipients.validateRecipients(count, myArgs); });
Saya kemudian memanggil fungsi validateRecipients. Perhatikan bahwa fungsi ini bersifat asynchronous. Setelah memvalidasi bahwa infile dan outfile adalah CSV, saya menulis baris header, dan memulai timer program menggunakan pustaka JSDOM.
async function validateRecipients(email_count, myArgs) { if ( // If both the infile and outfile are in .csv format extname(myArgs[1]).toLowerCase() == ".csv" && extname(myArgs[3]).toLowerCase() == ".csv" ) { let completed = 0; // Counter for each API call email_count++; // Line counter returns #lines - 1, this corrects the number of lines // Start a 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" ); // Write the headers in the outfile } }
Script berikut adalah inti dari program, jadi saya akan memecahnya dan menjelaskan apa yang terjadi. Untuk setiap baris dari infile:
Secara asynchronous ambil baris itu dan panggil API validasi penerima.
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, }, }); // For each row read in from the infile, call the SparkPost Recipient Validation API });
Kemudian, pada respons
Tambahkan email ke JSON (agar dapat mencetak email dalam CSV)
Validasi jika alasan adalah null, dan jika ya, isi dengan nilai kosong (ini untuk menjaga konsistensi format CSV, karena dalam beberapa kasus alasan diberikan dalam respons)
Atur opsi dan kunci untuk modul json2csv.
Ubah JSON menjadi 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); // Adds the email as a value/key pair to the response JSON for output response.data.results.reason ? null : (response.data.results.reason = ""); // If reason is null, set it to blank so the CSV is uniform // Utilizes json-2-csv to convert the JSON to CSV format and output let options = { prependHeader: false, // Disables JSON values from being added as header rows for every line keys: [ "results.email", "results.valid", "results.result", "results.reason", "results.is_role", "results.is_disposable", "results.is_free", "results.delivery_confidence", ], // Sets the order of keys }; let json2csvCallback = function (err, csv) { if (err) throw err; output.write(`${csv}\n`); }; converter.json2csv(response.data, json2csvCallback, options); completed++; // Increase the API counter process.stdout.write(`Done with ${completed} / ${email_count}\r`); // Output status of Completed / Total to the console without showing new lines // If all emails have completed validation if (completed == email_count) { const stop = window.performance.now(); // Stop the timer console.log( `All emails successfully validated in ${(stop - start) / 1000} seconds` ); } });
Satu masalah terakhir yang saya temui adalah saat ini berjalan dengan baik di Mac, saya mengalami kesalahan berikut saat menggunakan Windows setelah sekitar 10.000 validasi:
Error: connect ENOBUFS XX.XX.XXX.XXX:443 – Local (undefined:undefined) dengan email XXXXXXX@XXXXXXXXXX.XXX
Setelah melakukan beberapa penelitian lebih lanjut, sepertinya ini adalah masalah dengan kolam koneksi klien HTTP NodeJS yang tidak menggunakan kembali koneksi. Saya menemukan artikel Stackoverflow tentang masalah ini, dan setelah menyelidiki lebih lanjut, menemukan konfigurasi default yang baik untuk pustaka axios yang menyelesaikan masalah ini. Saya masih tidak yakin mengapa masalah ini hanya terjadi di Windows dan bukan di Mac.
Setelah membaca dan memvalidasi argumen terminal, saya menjalankan kode berikut. Pertama, saya membaca file CSV dari email dan menghitung setiap baris. Ada dua tujuan dari fungsi ini, 1) ini memungkinkan saya untuk melaporkan kemajuan file dengan akurat [seperti yang akan kita lihat nanti], dan 2) ini memungkinkan saya untuk menghentikan timer ketika jumlah email dalam file sama dengan validasi yang selesai. Saya menambahkan timer agar saya dapat menjalankan benchmark dan memastikan saya mendapatkan hasil yang baik.
let count = 0; // Line count require("fs") .createReadStream(myArgs[1]) .on("data", function (chunk) { for (let i = 0; i < chunk.length; ++i) if (chunk[i] == 10) count++; }) // Reads the infile and increases the count for each line .on("close", function () { // At the end of the infile, after all lines have been counted, run the recipient validation function validateRecipients.validateRecipients(count, myArgs); });
Saya kemudian memanggil fungsi validateRecipients. Perhatikan bahwa fungsi ini bersifat asynchronous. Setelah memvalidasi bahwa infile dan outfile adalah CSV, saya menulis baris header, dan memulai timer program menggunakan pustaka JSDOM.
async function validateRecipients(email_count, myArgs) { if ( // If both the infile and outfile are in .csv format extname(myArgs[1]).toLowerCase() == ".csv" && extname(myArgs[3]).toLowerCase() == ".csv" ) { let completed = 0; // Counter for each API call email_count++; // Line counter returns #lines - 1, this corrects the number of lines // Start a 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" ); // Write the headers in the outfile } }
Script berikut adalah inti dari program, jadi saya akan memecahnya dan menjelaskan apa yang terjadi. Untuk setiap baris dari infile:
Secara asynchronous ambil baris itu dan panggil API validasi penerima.
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, }, }); // For each row read in from the infile, call the SparkPost Recipient Validation API });
Kemudian, pada respons
Tambahkan email ke JSON (agar dapat mencetak email dalam CSV)
Validasi jika alasan adalah null, dan jika ya, isi dengan nilai kosong (ini untuk menjaga konsistensi format CSV, karena dalam beberapa kasus alasan diberikan dalam respons)
Atur opsi dan kunci untuk modul json2csv.
Ubah JSON menjadi 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); // Adds the email as a value/key pair to the response JSON for output response.data.results.reason ? null : (response.data.results.reason = ""); // If reason is null, set it to blank so the CSV is uniform // Utilizes json-2-csv to convert the JSON to CSV format and output let options = { prependHeader: false, // Disables JSON values from being added as header rows for every line keys: [ "results.email", "results.valid", "results.result", "results.reason", "results.is_role", "results.is_disposable", "results.is_free", "results.delivery_confidence", ], // Sets the order of keys }; let json2csvCallback = function (err, csv) { if (err) throw err; output.write(`${csv}\n`); }; converter.json2csv(response.data, json2csvCallback, options); completed++; // Increase the API counter process.stdout.write(`Done with ${completed} / ${email_count}\r`); // Output status of Completed / Total to the console without showing new lines // If all emails have completed validation if (completed == email_count) { const stop = window.performance.now(); // Stop the timer console.log( `All emails successfully validated in ${(stop - start) / 1000} seconds` ); } });
Satu masalah terakhir yang saya temui adalah saat ini berjalan dengan baik di Mac, saya mengalami kesalahan berikut saat menggunakan Windows setelah sekitar 10.000 validasi:
Error: connect ENOBUFS XX.XX.XXX.XXX:443 – Local (undefined:undefined) dengan email XXXXXXX@XXXXXXXXXX.XXX
Setelah melakukan beberapa penelitian lebih lanjut, sepertinya ini adalah masalah dengan kolam koneksi klien HTTP NodeJS yang tidak menggunakan kembali koneksi. Saya menemukan artikel Stackoverflow tentang masalah ini, dan setelah menyelidiki lebih lanjut, menemukan konfigurasi default yang baik untuk pustaka axios yang menyelesaikan masalah ini. Saya masih tidak yakin mengapa masalah ini hanya terjadi di Windows dan bukan di Mac.
Setelah membaca dan memvalidasi argumen terminal, saya menjalankan kode berikut. Pertama, saya membaca file CSV dari email dan menghitung setiap baris. Ada dua tujuan dari fungsi ini, 1) ini memungkinkan saya untuk melaporkan kemajuan file dengan akurat [seperti yang akan kita lihat nanti], dan 2) ini memungkinkan saya untuk menghentikan timer ketika jumlah email dalam file sama dengan validasi yang selesai. Saya menambahkan timer agar saya dapat menjalankan benchmark dan memastikan saya mendapatkan hasil yang baik.
let count = 0; // Line count require("fs") .createReadStream(myArgs[1]) .on("data", function (chunk) { for (let i = 0; i < chunk.length; ++i) if (chunk[i] == 10) count++; }) // Reads the infile and increases the count for each line .on("close", function () { // At the end of the infile, after all lines have been counted, run the recipient validation function validateRecipients.validateRecipients(count, myArgs); });
Saya kemudian memanggil fungsi validateRecipients. Perhatikan bahwa fungsi ini bersifat asynchronous. Setelah memvalidasi bahwa infile dan outfile adalah CSV, saya menulis baris header, dan memulai timer program menggunakan pustaka JSDOM.
async function validateRecipients(email_count, myArgs) { if ( // If both the infile and outfile are in .csv format extname(myArgs[1]).toLowerCase() == ".csv" && extname(myArgs[3]).toLowerCase() == ".csv" ) { let completed = 0; // Counter for each API call email_count++; // Line counter returns #lines - 1, this corrects the number of lines // Start a 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" ); // Write the headers in the outfile } }
Script berikut adalah inti dari program, jadi saya akan memecahnya dan menjelaskan apa yang terjadi. Untuk setiap baris dari infile:
Secara asynchronous ambil baris itu dan panggil API validasi penerima.
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, }, }); // For each row read in from the infile, call the SparkPost Recipient Validation API });
Kemudian, pada respons
Tambahkan email ke JSON (agar dapat mencetak email dalam CSV)
Validasi jika alasan adalah null, dan jika ya, isi dengan nilai kosong (ini untuk menjaga konsistensi format CSV, karena dalam beberapa kasus alasan diberikan dalam respons)
Atur opsi dan kunci untuk modul json2csv.
Ubah JSON menjadi 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); // Adds the email as a value/key pair to the response JSON for output response.data.results.reason ? null : (response.data.results.reason = ""); // If reason is null, set it to blank so the CSV is uniform // Utilizes json-2-csv to convert the JSON to CSV format and output let options = { prependHeader: false, // Disables JSON values from being added as header rows for every line keys: [ "results.email", "results.valid", "results.result", "results.reason", "results.is_role", "results.is_disposable", "results.is_free", "results.delivery_confidence", ], // Sets the order of keys }; let json2csvCallback = function (err, csv) { if (err) throw err; output.write(`${csv}\n`); }; converter.json2csv(response.data, json2csvCallback, options); completed++; // Increase the API counter process.stdout.write(`Done with ${completed} / ${email_count}\r`); // Output status of Completed / Total to the console without showing new lines // If all emails have completed validation if (completed == email_count) { const stop = window.performance.now(); // Stop the timer console.log( `All emails successfully validated in ${(stop - start) / 1000} seconds` ); } });
Satu masalah terakhir yang saya temui adalah saat ini berjalan dengan baik di Mac, saya mengalami kesalahan berikut saat menggunakan Windows setelah sekitar 10.000 validasi:
Error: connect ENOBUFS XX.XX.XXX.XXX:443 – Local (undefined:undefined) dengan email XXXXXXX@XXXXXXXXXX.XXX
Setelah melakukan beberapa penelitian lebih lanjut, sepertinya ini adalah masalah dengan kolam koneksi klien HTTP NodeJS yang tidak menggunakan kembali koneksi. Saya menemukan artikel Stackoverflow tentang masalah ini, dan setelah menyelidiki lebih lanjut, menemukan konfigurasi default yang baik untuk pustaka axios yang menyelesaikan masalah ini. Saya masih tidak yakin mengapa masalah ini hanya terjadi di Windows dan bukan di Mac.
Langkah Selanjutnya
Untuk seseorang yang mencari program cepat sederhana yang menerima csv, memanggil API validasi penerima, dan mengeluarkan CSV, program ini untuk Anda.
Beberapa tambahan untuk program ini adalah sebagai berikut:
Membangun antarmuka depan atau UI yang lebih mudah digunakan
Pemrosesan error dan pengulangan yang lebih baik karena jika karena alasan tertentu API menghasilkan kesalahan, program saat ini tidak mencoba kembali panggilan tersebut
Pertimbangkan untuk mengimplementasikan sebagai Fungsi Azure tanpa server untuk penskalaan otomatis dan pengelolaan infrastruktur yang lebih sedikit
Saya juga penasaran untuk melihat apakah hasil yang lebih cepat dapat dicapai dengan bahasa lain seperti Golang atau Erlang/Elixir. Selain pilihan bahasa, batasan infrastruktur juga dapat mempengaruhi kinerja - kami telah belajar ini secara langsung ketika kami menghadapi batas DNS yang tidak terdokumentasi di AWS yang mempengaruhi sistem pemrosesan email volume tinggi kami.
Untuk pengembang yang tertarik menggabungkan pemrosesan API dengan alat alur kerja visual, lihat cara mengintegrasikan Flow Builder dengan Google Cloud Functions untuk alur kerja otomatis tanpa kode.
Silakan beri saya masukan atau saran untuk memperluas proyek ini.
Untuk seseorang yang mencari program cepat sederhana yang menerima csv, memanggil API validasi penerima, dan mengeluarkan CSV, program ini untuk Anda.
Beberapa tambahan untuk program ini adalah sebagai berikut:
Membangun antarmuka depan atau UI yang lebih mudah digunakan
Pemrosesan error dan pengulangan yang lebih baik karena jika karena alasan tertentu API menghasilkan kesalahan, program saat ini tidak mencoba kembali panggilan tersebut
Pertimbangkan untuk mengimplementasikan sebagai Fungsi Azure tanpa server untuk penskalaan otomatis dan pengelolaan infrastruktur yang lebih sedikit
Saya juga penasaran untuk melihat apakah hasil yang lebih cepat dapat dicapai dengan bahasa lain seperti Golang atau Erlang/Elixir. Selain pilihan bahasa, batasan infrastruktur juga dapat mempengaruhi kinerja - kami telah belajar ini secara langsung ketika kami menghadapi batas DNS yang tidak terdokumentasi di AWS yang mempengaruhi sistem pemrosesan email volume tinggi kami.
Untuk pengembang yang tertarik menggabungkan pemrosesan API dengan alat alur kerja visual, lihat cara mengintegrasikan Flow Builder dengan Google Cloud Functions untuk alur kerja otomatis tanpa kode.
Silakan beri saya masukan atau saran untuk memperluas proyek ini.
Untuk seseorang yang mencari program cepat sederhana yang menerima csv, memanggil API validasi penerima, dan mengeluarkan CSV, program ini untuk Anda.
Beberapa tambahan untuk program ini adalah sebagai berikut:
Membangun antarmuka depan atau UI yang lebih mudah digunakan
Pemrosesan error dan pengulangan yang lebih baik karena jika karena alasan tertentu API menghasilkan kesalahan, program saat ini tidak mencoba kembali panggilan tersebut
Pertimbangkan untuk mengimplementasikan sebagai Fungsi Azure tanpa server untuk penskalaan otomatis dan pengelolaan infrastruktur yang lebih sedikit
Saya juga penasaran untuk melihat apakah hasil yang lebih cepat dapat dicapai dengan bahasa lain seperti Golang atau Erlang/Elixir. Selain pilihan bahasa, batasan infrastruktur juga dapat mempengaruhi kinerja - kami telah belajar ini secara langsung ketika kami menghadapi batas DNS yang tidak terdokumentasi di AWS yang mempengaruhi sistem pemrosesan email volume tinggi kami.
Untuk pengembang yang tertarik menggabungkan pemrosesan API dengan alat alur kerja visual, lihat cara mengintegrasikan Flow Builder dengan Google Cloud Functions untuk alur kerja otomatis tanpa kode.
Silakan beri saya masukan atau saran untuk memperluas proyek ini.



