简体   繁体   English

使用C#中的MailKit发送批量电子邮件

[英]Send bulk email with MailKit in C#

We are using MailKit to successfully send emails by creating a client, making a connection and sending a mail. 我们正在使用MailKit通过创建客户端,建立连接和发送邮件来成功发送电子邮件。 Very standard stuff which works great as we receive a message in our application, we do something and send on an email. 非常标准的东西非常有用,因为我们在应用程序中收到一条消息,然后做一些事情并通过电子邮件发送。
However we want to be able to process 10's of thousands of emails as quickly as possible. 但是,我们希望能够尽快处理成千上万的电子邮件
Our destination email server may be unavailable and therefore we could quickly backup messages therefore producing a backlog. 我们的目标电子邮件服务器可能不可用,因此我们可以快速备份邮件,从而产生积压。

With MailKit , what is the best and quickwest way to process the mails so that they get sent as quickly as possible. 使用MailKit ,最好的和最快捷的方式是处理邮件,以便尽快发送邮件。 For example at the moment each mail may be processed one after the other and if they take a second each to process it could take a long time to send 40000 mails. 例如,此刻每个邮件可能一个接一个地处理,如果每个处理都花费一秒钟,则可能要花费很长时间才能发送40000封邮件。

We have been using a parallel foreach to spin up a number of threads but this has limitations. 我们一直在使用parallel foreach来启动许多线程,但这有其局限性。 Any suggestions or recommendations would be appreciated. 任何建议或建议,将不胜感激。

Code sample added: CORRECTION, NEW CODE SAMPLE ADDED. 已添加代码示例: 更正,已添加新代码示例。 This is much faster but I cannot get it to work creating a new connection each time. 这要快得多,但是每次都无法使它工作以创建新的连接。 Exchange throws errors 'sender already specified'. Exchange引发错误“已指定发件人”。 This is currently sending around 6 mails per second on average. 目前,平均每秒大约发送6封邮件。

            var rangePartitioner = Partitioner.Create(0, inpList.Count, 15);

            var po = new ParallelOptions { MaxDegreeOfParallelism = 30 };


            Parallel.ForEach(rangePartitioner, (range, loopState) =>
            {
                using (var client = new SmtpClient(new SlabProtocolLogger()))
                {

                    client.Connect(_appSettings.RelayAddress, _appSettings.RelayPort);
                    client.AuthenticationMechanisms.Remove("XOAUTH2");

                    for (int i = range.Item1; i < range.Item2; i++)
                    {

                        var message = _outboundQueueRepository.Read(inpList[i]).Load();
                        client.Send(message.Body, message.Metadata.Sender, message.Metadata.Recipients.Select(r => (MailboxAddress)r));

                        _outboundQueueRepository.Remove(inpList[i]);
                    };
                }

            });

Correct me if I'm wrong, but it looks to me like the way this works is that the Parallel.Foreach is creating some number of threads. 如果我错了,请纠正我,但是在我看来,这种工作方式是Parallel.Foreach正在创建一定数量的线程。 Each thread is then creating an SMTP connection and then looping to send a batch of messages. 然后,每个线程都会创建一个SMTP连接,然后循环发送一批消息。

This seems pretty reasonable to me. 这对我来说似乎很合理。

The only advice I can give you that might optimize this more is if many of these messages have the exact same content and the same From address and that the only difference is who the recipients are, you could vastly reduce the number of messages you need to send. 我可以提供给您的唯一建议可能会进一步优化此建议,即如果这些邮件中的许多邮件具有完全相同的内容和相同的“发件人”地址,并且唯一的区别是收件人是谁,则可以大大减少所需的邮件数量发送。

For example, if you are currently doing something like sending out the following 3 messages: 例如,如果您当前正在执行以下操作,例如发送以下3条消息:

Message #1: 讯息1:

From: no-reply@company.com
To: Joe The Plumber <joe@plumbing-masters.com>
Subject: We've got a new sale! 50% off everything in stock!

some message text goes here.

Message #2 讯息2

From: no-reply@company.com
To: Sara the Chef <sara@amazing-chefs.com>
Subject: We've got a new sale! 50% off everything in stock!

some message text goes here.

Message #3: 讯息3:

From: no-reply@company.com
To: Ben the Cobbler <ben@cobblers-r-us.com>
Subject: We've got a new sale! 50% off everything in stock!

some message text goes here.

Your code might create 3 threads, sending 1 of the messages in each of those threads. 您的代码可能会创建3个线程,并在每个线程中发送一条消息。

But what if, instead, you created the following single message: 但是,相反,如果您创建了以下一条消息,该怎么办:

From: no-reply@company.com
To: undisclosed-recipients:;
Subject: We've got a new sale! 50% off everything in stock!

some message text goes here.

and then used the following code to send to all 3 customers at the same MimeMessage ? 然后使用以下代码通过同一MimeMessage发送给所有3个客户?

var sender = new MailboxAddress (null, "no-reply@company.com");
var recipients = new List<MailboxAddress> ();
recipients.Add (new MailboxAddress ("Joe the Plumber", "joe@plumbing-masters.com"));
recipients.Add (new MailboxAddress ("Sara the Chef", "sara@amazing-chefs.com"));
recipients.Add (new MailboxAddress ("Ben the Cobbler", "ben@cobblers-r-us.com"));

client.Send (message, sender, recipients);

All 3 of your customers will receive the same email and you didn't have to send 3 messages, you only needed to send 1 message. 您的所有3个客户都将收到同一封电子邮件,您不必发送3条消息,只需要发送1条消息即可。

You may already understand this concept so this might not help you at all - I merely mention it because I've noticed that this is not immediately obvious to everyone. 您可能已经了解了这个概念,所以这可能根本对您没有帮助-我只是提到它,因为我注意到这并不是每个人都立即意识到的。 Some people think they need to send 1 message per recipient and so end up in a situation where they try to optimize sending 1000's of messages when really they only ever needed to send 1. 有些人认为他们需要向每个收件人发送1条消息,因此最终导致他们实际上只需要发送1条消息时就尝试优化发送1000条消息。

So we found a wider ranging fix which improved performance massively in addition to the improvements we found with the Parrallel ForEach loop. 因此,我们发现了范围更广的修复程序,除了通过Parrallel ForEach循环发现的改进之外,还大大提高了性能。 Not related to MailKit but I thought I would share anyway. 与MailKit无关,但我想还是应该分享。 The way that our calling code was creating our inputList was to use DirectoryInfo.GetDirectories to enumerate over all the first in the directory. 调用代码创建inputList的方式是使用DirectoryInfo.GetDirectories枚举目录中的所有第一个。 In some cases our code took 2 seconds to execute over the directory with 40k files in it. 在某些情况下,我们的代码需要2秒钟才能在其中包含40k文件的目录上执行。 We changed this to EnumerateDirectories and it effecitvely freed up the mail sending code to send many many emails. 我们将其更改为EnumerateDirectories,它有效地释放了邮件发送代码以发送许多电子邮件。

So to confirm, the Parallel loop worked great, underlying performance issue elsewhere was the real bottleneck. 因此可以确认,并行循环非常有效,其他地方的根本性能问题是真正的瓶颈。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM