内存泄漏与symfony2& swiftmailer

时间:2014-02-07 00:42:25

标签: php symfony swiftmailer

我使用symfony2命令作为cron作业向群发成员发送批量电子邮件。

实际代码:

$output->writeln('Before: '.(memory_get_usage() / 1024 / 1024));

$mailer->send($m);

$output->writeln('After: '.(memory_get_usage() / 1024 / 1024));

我的结果:

Before: 182.38 MB
After: 183.73 MB

每次发送电子邮件时,swiftmailer都会消耗额外的1 + MB内存。这似乎并不合适,但每次发送新消息时内存都会增加。我在这里做错了吗?

3 个答案:

答案 0 :(得分:5)

SwiftMailer内存假脱机系统

您指出的问题实际上更多的是解决方案而不是问题。你没有做错任何事,这是由于SwiftMailer内部发送电子邮件的方式。

根据send()方法,SwiftMailer实际上并不发送任何内容,而只是将其放入他的假脱机中。默认情况下,假脱机选项为memory,并且在内核终止之前发生假脱机刷新。所以memory_get_usage()没有机会告诉你内存已经被释放(因为很明显,你的脚本仍在运行,并且SwiftMailer还没有刷新它的假脱机)。

来自Symfony2文档:

  

当您使用假脱机将电子邮件存储到内存时,它们会得到   在内核终止之前发送。这仅表示电子邮件   如果整个请求执行没有任何未处理,则会被发送   例外或任何错误。

使用文件假脱机系统

如果此假脱机选项导致您遇到麻烦,您可以切换到File假脱机选项。

可以通过更改此内容来完成:

# app/config/config.yml
swiftmailer:
    # ...
    spool: { type: memory }

对此:

# app/config/config.yml
swiftmailer:
    # ...
    spool:
        type: file
        path: /path/to/spool

然后,您可以将cron作业配置为自动并定期刷新假脱机:

$ php app/console swiftmailer:spool:send --env=prod

不使用假脱机

或者,您可以选择根本不使用假脱机系统,这意味着您在执行脚本期间将发送所有电子邮件。它可以帮助您摆脱内存问题,但可能会损害您的用户,具体取决于您是否在请求页面时发送邮件。但是,正如你在cron工作中所做的那样,在你的情况下它肯定不会是一个问题。可能是有一天。

答案 1 :(得分:5)

我在使用swiftmailer发送电子邮件时遇到了类似的内存问题。似乎插件会创建这些内存泄漏,因此不必删除事件调度程序代码,只需禁用插件(对于您的情况,使用您自己的解决方案进行修补)。

使用simfony2,在配置中轻松禁用日志记录:

swiftmailer:
    logging: false

或使用多个邮寄者:

swiftmailer:
    mailers:
        my_mailer:
            logging: false

在我的情况下,发送~1500封电子邮件,内存增长到大约100MB(仅仅因为记录器),现在> 0.2MB。

我知道这有点晚了,但也许这对遇到同样问题的人有用。 : - )

答案 2 :(得分:-1)

编辑:不确定为什么用户正在进行投票。事实是swiftmailer本身有一个与其事件/插件系统直接相关的内存泄漏。当钩子被执行时,它会占用永远不会释放的内存。仅仅删除事件系统就可以修复内存泄漏,而不会破坏swiftmailer在实际发送电子邮件时的工作方式。

我解决了这个问题。我编辑了MailTransport类并删除了swiftmailer中的事件系统。我只允许构建和发送消息本身,没有前后插件废话。

我从throttlerplugin中获取了部分并由自己的版本创建,它专门只处理每分钟的消息:

class EmailThrottler {

    private $startTime;
    private $messagesPerMinute;
    private $_messages;

    public function __construct($messagesPerMinute = 25)
    {
        $this->startTime = time();
        $this->messagesPerMinute = $messagesPerMinute;
        $this->_messages = 0;
    }

    public function run()
    {
        $this->_messages++;
        $duration = time() - $this->startTime;
        $sleep = $this->_throttleMessagesPerMinute($duration);

        if ($sleep > 0) {
            sleep($sleep);
        }
    }

    private function _throttleMessagesPerMinute($duration)
    {
        $expectedDuration = $this->_messages / ($this->messagesPerMinute / 60);
        return (int) ceil($expectedDuration - $duration);
    }

}

我在邮件循环之前初始化了我的自定义throttler类:

$throttler = new EmailThrottler($pendingCampaign->getRateLimit());

我在每封发送的电子邮件后运行它:

$mailer->send($m);
$throttler->run();

希望他们能找到对事件系统的修复。无论如何,我会看看5.4的表现是否有任何不同,但对于那些在5.3上的人来说这个解决方案我只是在5.3上为我工作了

干杯:)