构建一个批量异步鸟类接收者验证工具
扎卡里·萨缪尔斯
2022年5月26日
电子邮件
1 min read

关键要点
The author built a bulk recipient validation tool to validate millions of email addresses efficiently using Bird’s Recipient Validation API.
Node.js proved faster and more scalable than Python due to its non-blocking I/O and lack of GIL limitations.
The tool reads CSV files asynchronously, calls the validation API for each email, and writes results to a new CSV in real time.
The approach avoids memory bottlenecks and improves throughput to about 100,000 validations in under a minute.
Future improvements could include better retry handling, a user-friendly UI, or migrating to serverless environments for scalability.
Q&A Highlights
What is the purpose of the Bulk Asynchronous Recipient Validation Tool?
It validates large volumes of email addresses by integrating directly with Bird’s Recipient Validation API, outputting verified results quickly without manual uploads.
Why was Python initially used and later replaced by Node.js?
Python’s Global Interpreter Lock (GIL) limited concurrency, while Node.js allowed true asynchronous execution, resulting in far faster parallel API calls.
How does the tool handle large files without running out of memory?
Instead of loading all data at once, the script processes each CSV line individually—sending the validation request and immediately writing results to a new CSV file.
What problem does the tool solve for developers?
It enables email list validation at scale, overcoming the 20MB limit of SparkPost’s UI-based validator and eliminating the need to upload multiple files manually.
How fast is the final version of the program?
Around 100,000 validations complete in 55 seconds, compared to over a minute using the UI version.
What issues were encountered on Windows systems?
Node.js HTTP client connection pooling caused “ENOBUFS” errors after many concurrent requests, which were fixed by configuring axios connection reuse.
What future enhancements are suggested?
Adding error handling and retries, creating a front-end interface, or implementing the tool as a serverless Azure Function for better scalability and resilience.
对于那些寻找一个简单快速的程序的人,该程序可以接受csv,调用收件人验证API,并输出CSV,这个程序就适合你。
在构建电子邮件应用程序时,开发人员通常需要集成多个服务和API。了解云基础设施中的电子邮件API基础为构建像我们将在本指南中创建的大量验证系统这样的强大工具提供了基础。
我们偶尔会收到的问题之一是,我如何使用收件人验证批量验证电子邮件列表?这里有两个选项,一个是通过SparkPost用户界面上传文件进行验证,另一个是对每个电子邮件单独调用API(因为API是单个电子邮件验证)。
第一个选项效果很好,但有20MB(约500,000个地址)的限制。如果某人的电子邮件列表包含数百万个地址怎么办?这可能意味着要将其拆分为数千个CSV文件上传。
由于上传数千个CSV文件似乎有点牵强,我考虑了这种使用情况,并开始想知道我能让API运行得有多快。在这篇博客文章中,我将解释我尝试了什么以及如何最终成为一个可以在55秒内完成大约100,000次验证的程序(而在用户界面中,我大约用了1分10秒完成了100,000次验证)。尽管这仍然需要大约100小时才能完成约6.54亿次验证,但这个脚本可以在后台运行,显著节省时间。
该程序的最终版本可以在这里找到。
My first mistake: 使用 Python
Python 是我最喜欢的编程语言之一。它在许多领域表现出色,并且非常直观。然而,它在并发处理方面并不擅长。虽然 Python 确实具有运行异步函数的能力,但它拥有被称为 Python Global Interpreter Lock 或 GIL 的东西。
“Python Global Interpreter Lock 或 GIL,简单来说,是一个互斥锁(或一种锁),它只允许一个线程控制 Python 解释器。
这意味着在任意时间点只有一个线程可以处于执行状态。对于执行单线程程序的开发人员来说,GIL 的影响并不明显,但在 CPU 密集型和多线程代码中可能成为性能瓶颈。
由于 Global Interpreter Lock (GIL) 只允许一个线程同时执行,即使在多核系统上,它已成为 Python 的一个“臭名昭著”的功能(参见 Real Python 对 GIL 的文章)。
起初,我并不了解 GIL,于是开始使用 Python 编程。最后,即使我的程序是异步的,它还是被锁住了,无论我增加多少个线程,我每秒仍只能获得大约 12-15 个迭代。
Python 中异步函数的主要部分如下所示:
所以我放弃使用 Python,回到了起点……
我决定使用 NodeJS,因为它非常擅长执行非阻塞 I/O 操作。另一个处理异步 API 处理的极好选项是使用 Azure Functions 构建 无服务器的 webhook 消费者,它可以有效地处理可变的工作负载。我对 NodeJS 编程也相当熟悉。
利用 Node.js 的异步特性,这种方法效果很好。有关 Node.js 中异步编程的更多细节,请参见 RisingStack 关于 Node.js 异步编程的指南。
我的第二个错误:尝试将文件读入内存
分解最终代码
在读取并验证终端参数后,我运行以下代码。首先,我读取电子邮件的CSV文件并计算每行。这个函数有两个目的:1) 它可以让我准确报告文件进度【如我们稍后将看到的】,2) 它使我能够在文件中的电子邮件数量等于完成验证的数量时停止计时。我添加了计时器,以便能够运行基准测试并确保获得良好的结果。
然后我调用validateRecipients函数。 注意这个函数是异步的。在验证infile和outfile是CSV后,我写入一个标题行,并使用JSDOM库启动一个程序计时器。
以下脚本实际上是程序的主体,因此我将其分解并进行解释。对于infile的每一行:
异步地获取该行并调用recipient validation API。
然后,在响应上
将电子邮件添加到JSON中(以便能够在CSV中打印出电子邮件)
验证原因是否为空,如果为空,则填充一个空值(这是为了使CSV格式一致,因为在某些情况下响应中给出了原因)
设置json2csv模块的选项和键。
转换JSON为CSV并输出(利用json2csv)
在终端中写入进度
最后,如果文件中的电子邮件数量=完成的验证,停止计时器并打印出结果
我发现的一个最后问题是,虽然这在Mac上效果很好,但在Windows上使用约10,000次验证后遇到了以下错误:
Error: connect ENOBUFS XX.XX.XXX.XXX:443 – Local (undefined:undefined) with email XXXXXXX@XXXXXXXXXX.XXX
经过进一步研究,似乎是NodeJS HTTP客户端连接池没有重用连接的问题。我在这个问题上找到了一篇Stackoverflow文章,并在进一步挖掘后,找到了一个适合解决该问题的axios库默认配置。我仍然不确定为何该问题只发生在Windows上而不是Mac上。
下一步
对于正在寻找一个简单快速的程序的人,该程序接收CSV,调用收件人验证API,并输出一个CSV,这个程序就是为你准备的。
对此程序的一些补充功能包括以下内容:
构建一个前端或更简单的用户界面以供使用
更好的错误处理和重试机制,因为如果由于某种原因API抛出错误,当前程序不会重试调用
考虑将其实现为无服务器Azure Function,以实现自动扩展和减少基础设施管理
我也很想知道是否可以通过其他语言(比如Golang或Erlang/Elixir)实现更快的结果。除了语言选择,基础设施限制也可能影响性能——我们亲身体验到了这一点,当时我们在AWS遇到了未记录的DNS限制,这影响了我们的高容量电子邮件处理系统。
对于对将API处理与视觉工作流程工具结合的开发人员,请查看如何整合Flow Builder与Google Cloud Functions进行无代码自动化工作流程。
请随时向我提供任何反馈或建议,以扩展此项目。





