首页 文章

测试高容量SMTP电子邮件发送代码的最佳方法?

提问于
浏览
6

我在Windows服务(C#)中编写了一个组件,负责发送大量的电子邮件 . 这些电子邮件将发送到许多域的收件人 - 实际上,任何域 . (是的,收件人想要收到的电子邮件 . 不,我是'm not spamming. Yes, I' m抱怨CAN-SPAM . 是的,我知道sending email from code sucks . )许多电子邮件都是事务性的(响应用户操作而生成);一些是批量的(基本上是邮件合并) .

我不想依赖外部SMTP服务器 . (除了其他考虑因素之外,想要检查邮箱的弹跳消息并尝试解析它们的想法让我感觉不好 . )

我的设计相当简单 . 生成事务和批量消息都会生成并插入到DB表中 . 此表包含电子邮件信封和内容,以及尝试计数和重试日期 .

该服务运行一些工作线程,每次抓取20行并循环遍历每个 . 使用Simple DNS Plus库,我获取收件人域的MX记录,然后使用 System.Net.Mail.SmtpClient 同步发送电子邮件 . 如果对 Send() 的调用成功,我可以将电子邮件出列 . 如果它暂时失败,我可以递增尝试次数并设置适当的重试次数 . 如果它永久失败,我可以出列并处理失败 .

显然,将数千个测试电子邮件发送到数百个不同的实际域名是一个非常糟糕的主意 . 但是,我绝对需要对我的多线程发送代码进行压力测试 . 我也不太确定模拟SMTP各种故障模式的最佳方法是什么 . 另外,我想确保通过各种垃圾邮件控制方法(灰名单命名与网络层最相关) .

甚至我的小规模测试困难也因我最近发现我的ISP阻止连接到 any 服务器上的端口25而非我的ISP 's SMTP server. (In production, this thing will of course be on a proper server where port 25 isn't阻塞而加剧 . 这对我的开发机没有帮助 . )

所以,我最好奇的两件事:

  • 我该如何测试我的代码?

  • SmtpClient.Send() 失败的各种方式有哪些?列出了六个例外; SmtpExceptionSmtpFailedRecipientsException 似乎是最相关的 .


Update: Marc B's answer指出我重新发明轮子,所以这里's my rationale for not using an '实际'一个(Postfix等)代替:

  • 电子邮件具有不同的发送优先级(尽管这与信封的 X-Priority 无关) . 批量电子邮件是低优先级;交易很高 . (并且可以进一步配置任何电子邮件或电子邮件组以具有任意优先级 . )我需要能够暂停发送优先级较低的电子邮件,以便可以首先发送更高优先级的电子邮件 . (为了实现这一点,工作者线程只需在每次获得另一个20时从队列中获取最高优先级的项目 . )

如果我已经向外部SMTP服务器提交了数千个批量项目,那么当我现在要提交的项目被发送时,我无法将这些项目保留 . 粗略的Google search显示Postfix并不真正支持优先级; Sendmail优先处理信封中的信息,这不符合我的需要 .

  • 我需要能够向我的用户显示一个爆炸(一组批量电子邮件)的发送过程的进度 . 如果我只是将我的所有电子邮件发送到外部服务器,我不知道它在实际交付方面有多远 .

  • 我'm hesitant to parse bounce messages because each MTA'的退回讯息不同 . Sendmail 's is different from Exchange'与 [...] 不同 . 另外,我以什么频率检查我的反弹收件箱?如果退回邮件本身未送达怎么办?

  • 我并不太关心爆炸中途失败 .

如果我们谈论灾难性的失败(应用程序终止未处理的异常,电源故障,等等):由于工作线程在成功传递时将每个电子邮件从数据库中取出,我可以知道谁收到了爆炸,谁没有 . 此外,当服务在失败后重置时,它只是从队列中的中断位置开始 .

如果我们正在谈论本地故障( SmtpException ,DNS故障等):我只记录故障,增加电子邮件的尝试计数器,然后再试 . (这基本上是SMTP规范所要求的 . )在n次尝试之后,我可以永久地使消息失败(将其出列)并记录失败以便稍后进行检查 . 通过这种方式,我可以找到奇怪的边缘情况,我的代码第一次100%完美无法完成 . (让's be honest, it won' t是 . )

  • 如果服务器仍然是瓶颈,我不得不担心速率限制 . 多线程体系结构我可以并行连接多个远程服务器,从而减少了传递n条消息所需的总时间 .

2 回答

  • 0

    假设您有两台服务器可用 . 一个是发送者,一个是接收者 . 您可以使用一系列假域来设置DNS(甚至只是托管文件) . 就这两个服务器而言,这些域完全有效,因为本地DNS服务器对它们具有权威性,但就网络的其余部分而言完全无效 . 只需确保解析器在DNS之前检查hosts文件 .

    一旦完成,您可以让发送服务器将接收服务器垃圾邮件发送给您的内容,因为接收方会做各种事情来测试您的代码的反应 . 灰名单,TCP延迟,硬反弹,ICMP不可达,ICMP跳数超标等...

    当然,鉴于您必须测试所有这些条件,您基本上是在创建自己的SMTP服务器,那么为什么不使用实际的服务器呢?我猜想对弹跳消息进行一些基本解析所需的工作量远远少于提供代码块来处理postfix / sendmail / exim / etc等所有失败模式...已经完全处理好了拥有 .

    当你认为你的发送代码必须从一开始就是完美的时候尤其如此 . 如果电子邮件爆炸中途失败并且只有一半的收件人列表收到消息,那么您所处的漏洞要远远超过几百或几千条消息的反弹 . 或者更糟糕的是,以多种不同的方式失败(某些服务器无法访问,有些服务器会过度流量等等) . 反弹将很快放在传入的队列中,直到您手动处理它们,或修补您的反弹解析器来处理它们 .

  • 2

    searching around之后,我最终在我放置的几台额外的机器上启动Papercut . 然后我用测试地址 *@[test-machine-*.local] 填充我的数据库 .

    虽然这确实运行得很好,但我测试了25个发送线程,看起来我压倒了运行Papercut的四台计算机 . 数百次发送尝试遇到TCP连接失败;这些消息被正确地重新排队以便稍后发送(最终确实到达) . 然而,在25,000个测试电子邮件中,大约500个消失了 - 在每个测试机器的Papercut文件夹中添加* .eml文件仅产生~24,500 .

    现在我想知道丢失的电子邮件是否是由于我的代码中的问题,或者Papercut丢弃了它在SMTP中报告为 250 OK 的邮件 .

相关问题