【问题标题】:Multi-threading to speed up an email-sending application多线程加速电子邮件发送应用程序
【发布时间】:2012-01-01 23:08:58
【问题描述】:

我已经构建了一个应用程序,用于通过 Amazon SES 为网站发送电子邮件。它是用 C# 编码的。

通过 Amazon SES API 发送每封电子邮件需要 0.3 秒。 这意味着,使用单线程应用程序,我每秒只能发送 3 封电子邮件。

我已经实现了一个生产者/消费者、多线程应用程序,其中 1 个生产者为每个客户查询自定义电子邮件,以及 25 个消费者从队列中拉出并发送电子邮件。

我的多线程应用程序每秒发送 12 封电子邮件(速度提高了四倍)。我本来预计 25 线程应用程序的速度会更快。

我的问题是: 在单处理器机器上发送邮件的速度究竟能提高多少?我的收益是否合理,或者我的速度问题更有可能是由于编码而不是计算机无法快速处理电子邮件?

提前致谢!

更新: 如果其他人面临同样的问题....连接到 AWS 以发送电子邮件会占用大量时间。 AWS 开发人员论坛上的以下主题提供了一些见解(您可能需要向下滚动才能找到更有用的帖子)。

https://forums.aws.amazon.com/thread.jspa?threadID=78737

【问题讨论】:

  • 根据我对多线程的理解,它可以处理多个任务,但仍然需要相同的时间。所以我认为使用多线程不会节省大量时间。但是,如果在发送电子邮件时仍需要访问 UI,则可以使用多线程。
  • 你的系统有多少个内核?
  • 在典型的邮件发送期间队列计数会发生什么变化?看看生产者线程是否优于电子邮件发送者可能会很有趣。可能每秒将 P-C 队列计数转储到计时器上的屏幕上。
  • @MartinJames 是的,生产者线程的性能远远优于消费者......生产者仅在发送了几封电子邮件后就完成了电子邮件的格式化。

标签: c# multithreading performance amazon-web-services producer-consumer


【解决方案1】:

即使它是单处理器机器,你也可以加快速度。

发送电子邮件不会消耗大量CPU,这是一个IO绑定操作。 因此,您将通过并行执行工作大大提高您的性能。

【讨论】:

  • +1 可在多线程 I/O 等待的单处理器机器上实现大幅加速。
  • @SurjitSamra - 不知道你在问什么。他使用了一些我不熟悉的 Amazon API。
  • 你怎么能否认你自己的说法?阅读您自己答案的最后一行,您说“通过并行工作,您将大大提高您的性能。”所以我问你如何建议这样做?
  • @SurjitSamra - 在我的帖子中,我不建议任何实现。无论如何,并行处理可以通过多种方式完成,例如 - .NET TPL、创建新线程、使用 ThreadPool、调用方法的异步实现(BeginXXX、EndXXX)、事件基础模式
  • 速度肯定会通过使用多个并行线程来提高,如原始帖子中所述。该应用程序现在的速度是原来的 4 倍,但我会假设会有更大的增长......不是吗?
【解决方案2】:

我在博客中介绍了我的解决方案。基本上你使用Parallel.ForEach 循环和MaxDegreeOfParallelism,不要忘记增加app.config 中的maxconnection 计数。

以下是app.config 示例:

<system.net>
    <connectionManagement>
        <add address="*" maxconnection="392" />
    </connectionManagement>
    <mailSettings>
        <smtp from="form@company.com" deliveryMethod="Network">
            <network host="email-smtp.us-east-1.amazonaws.com" userName="SmtpUsername" password="SmtpPassword" enableSsl="true" port="587" />
        </smtp>
    </mailSettings>
</system.net>

这是Parallel.ForEach 循环示例:

class Program
{
    static readonly object syncRoot = new object();
    private readonly static int maxParallelEmails  = 196;

    static void Main(string[] args)
    {

        IList<Model.SendEmailTo> recipients = _emailerService.GetEmailsToSend();
        int cnt = 0;
        int totalCnt = recipients.Count;


        Parallel.ForEach(recipients.AsParallel(), new ParallelOptions { MaxDegreeOfParallelism = maxParallelEmails }, recipient =>
        {
            // Do any other logic

            // Build the email HTML

            // Send the email, make sure to log exceptions

            // Track email, etc

            lock (syncRoot) cnt++;
            Console.WriteLine(String.Format("{0}/{1} - Sent newsletter email to: {2}", cnt, totalCnt, recipient.Email));
        });
    }
}

我的博客有更详细的解释:http://michaeldimoudis.com/blog/2013/5/25/reliably-and-speedily-send-mass-emails-via-amazon-ses-in-c

【讨论】:

    【解决方案3】:

    我的问题是:我真的可以加快多少邮件的发送速度 在单处理器机器上?我的收益是否合理,或者是我的 速度问题更可能是由于编码而不是计算机 无法更快地处理电子邮件?

    从广义上讲,线程数增加 25 倍,速度提高 4 倍并不过分,但也不是很好。

    只有当你的 CPU 使用率很高时,单个 CPU 才会成为瓶颈。您可以通过查看应用程序运行时的总 CPU 使用情况来判断这是否对您来说是个问题。理论上,群发邮件应该是 I/O 受限的操作;如果您不是这种情况,那么您的代码可能有问题。

    虽然我没有使用过 Amazon SES,但我知道其他 Amazon 产品肯定会使用各种形式的带宽/请求限制。有可能(很可能)您的吞吐量受到亚马逊的限制,而不是您的应用。

    不久前我写了一个高性能的群发邮件应用程序,我所做的是:

    1. 除了多线程之外,尽可能多地使用异步 I/O。这样,如果一个请求很慢,它就不会消耗整个线程。
    2. 将电子邮件直接发送到最终服务器,而不是通过中间网关。这需要使用 P/Invoke 调用 DNS 以检索必要的 MX 或 A 记录。之后,我使用标准的SmtpClient 类(它有一个SendAsync 方法)来实际发送邮件。

    这种方法还可以让我在发送邮件时查看和记录错误,从而为用户提供更好的反馈。另一种方法是依靠从网关服务器接收和解析错误邮件,这至少可以说是容易出错的。

    【讨论】:

    • +1 - 到目前为止,这是最能解决问题并提供最详细和具体信息的答案。谢谢!将尝试其中一些建议并返回此主题发布结果!
    【解决方案4】:

    在多核(或多处理器)系统上运行的多线程应用程序中,黄金法则是(通常)您无法获得比 N 倍顺序执行时间更好的加速,其中 N 是内核数。因此,如果您有一个需要 12 秒的活动,并且您在 4 个内核上并行运行它,那么总共不会超过 3 秒。

    相反,如果以前您可以在单个时间单位内执行一项活动,那么使用 4 个内核,您在同一时间单位内执行 4 个活动就无法做得更好。

    此外,由于以下几个因素通常会影响并行程序的性能:磁盘 I/O 瓶颈、内存饱和、锁争用等,并不总是能达到这个上限。

    【讨论】:

    • 这仅适用于 CPU 受限问题。 IE。我编写了并行查询服务器的代码,它的加速比比内核数量高得多(并且还使用更多线程)。但是如果没有分析瓶颈在哪里,我们就无能为力了..
    • 这个答案不适用于OP的情况。
    【解决方案5】:

    只有一个队列的生产者消费者不能很好地扩展。随着您添加更多消费者或生产者,队列将成为瓶颈。

    如果您有多处理器架构,您可以使用多个进程来发送电子邮件。你仍然可以使用你的生产者消费者多线程版本,但现在它将是一个 foreach 进程;这会加快速度(正如 Tudor 解释的那样),但问题仍然存在。

    但是,对于整个系统,您可能只有一个网络管理器或类似的实体来发送消息(例如 http 消息)和一个网卡。现在瓶颈可能是这个网络管理器。我想了解更多关于系统架构的信息:)

    【讨论】:

    • 与建立 TCP 连接和发送电子邮件所花费的 I/O 时间和延迟相比,花费在 P-C 队列活动上的时间是微不足道的。如果队列操作成为这个应用程序的瓶颈,我会感到惊讶。
    • 你不能保证生产者不会花费“很多”时间。我同意你,网络管理员可能是一个瓶颈。然而,重点仍然是队列方法不能很好地适应大量消费者和消费者
    • 与消费者相比,生产者完成得非常快......这似乎不是瓶颈,而是一个好点。
    • @Rebecca 很高兴知道更多。似乎您的应用程序速度不能超过 4 倍,因为消费者无法更快地发送电子邮件。马丁说的对,瓶颈是连接。检查队列(其大小)。我希望它会继续增长……如果你生产很多的话,可能会进入记忆之外的地方。
    • @Adrian 所以你认为我可能内存不足,因此队列操作需要更长的时间?如果队列变得太大,那么稍微减慢生产者的速度是否有意义?
    【解决方案6】:

    几个月前我也遇到过类似的情况。虽然我们需要很多因素来告诉您导致性能下降的原因,但您可以尝试使用 EC2 实例的 mirco 实例来尝试发送电子邮件。

    这在我的情况下效果很好,而且当我在 Web 应用程序上工作时,这是一个合适的解决方案。

    【讨论】:

      【解决方案7】:

      该任务既不受 CPU 限制,也不受 IO 限制。 该任务向 SES 发出请求以发送电子邮件(数据或 IO 有限),然后等待。因此,请使用可用于可用 RAM 的最大线程数。

      【讨论】:

        【解决方案8】:

        评论如何,这是一个 I/O 问题,因为,您需要找到大量具有基础设施/带宽大小的作业

        使用队列模式,

        例子:

        1 - 排队发送电子邮件

        2 - “N”个职位发送电子邮件

        【讨论】:

        • 这没什么意义。
        猜你喜欢
        • 2015-01-18
        • 1970-01-01
        • 2021-09-19
        • 2010-12-02
        • 1970-01-01
        • 1970-01-01
        • 2021-06-08
        • 2020-05-24
        • 2011-05-08
        相关资源
        最近更新 更多