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

jos*_*736 6 c# email testing smtp

我在Windows服务(C#)中编写了一个组件,负责发送大量的电子邮件.这些电子邮件将发送到许多域的收件人 - 实际上,任何域. (是的,收件人想要电子邮件.不,我不是垃圾邮件.是的,我对CAN-SPAM感到抱怨.是的,我知道从代码中发送电子邮件糟糕.) 许多电子邮件都是事务性的(生成的)响应用户的行为); 一些是批量的(基本上是邮件合并).

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

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

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

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

我最近发现我的ISP阻止连接到我的ISP的SMTP服务器以外的任何服务器上的端口25,甚至我的小规模测试困难也加剧了.(在生产中,这个东西当然会在一个没有阻塞端口25的正确服务器上.这对我的开发机器没有帮助.)

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

  1. 我该如何测试我的代码?
  2. SmtpClient.Send()可能失败的各种方法有哪些?列出了六个例外; SmtpException而且SmtpFailedRecipientsException似乎是最相关的.

更新: Marc B的答案指出我基本上是在创建自己的SMTP服务器.他提出了我正在重新发明轮子的有效观点,所以这是我不使用"实际"(Postfix等)的理由:

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

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

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

  3. 我对解析退回邮件犹豫不决,因为每个MTA的退回邮件都不同.Sendmail的不同于Exchange的不同[...].另外,我以什么频率检查我的反弹收件箱?如果退回邮件本身未送达怎么办?

  4. 我不是太担心爆炸中途失败了.

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

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

  5. 我希望滚动自己的路线最终会让我能够比我不得不依赖外部SMTP服务器更快地收到电子邮件.如果服务器不受我的控制,我不得不担心速率限制; 即使它是,它仍然是一个瓶颈.我已经使用的多线程架构意味着我将并行连接到多个远程服务器,从而减少了传递n条消息所需的总时间.

Mar*_*c B 2

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

一旦完成,您就可以让发送服务器向接收服务器发送垃圾邮件,就像让接收者做各种事情来测试代码的反应一样。灰名单、TCP 延迟、硬退回、ICMP 不可达、ICMP 跳数超出等...

当然,考虑到您必须测试所有这些条件,您基本上是在创建自己的 SMTP 服务器,那么为什么不一开始就使用实际的服务器呢?我猜想对退回消息进行一些基本解析所需的工作将远远少于必须拿出代码块来处理 postfix/sendmail/exim/等的所有故障模式...已经在其上完美处理自己的。

当您认为发送代码从一开始就必须完美时,这一点尤其正确。如果电子邮件群发中途失败,并且只有一半的收件人列表收到邮件,那么您面临的困境比几百或几千条邮件退回要大得多。或者更糟糕的是,以多种不同的方式失败(某些服务器无法访问,某些服务器因流量过多而将您列入灰名单,等等......)。而反弹会很高兴地坐在传入队列中,直到您手动处理它们,或者修补反弹解析器来处理它们。