【问题标题】:Perl mailqueueing with threads 'out of memory'线程“内存不足”的 Perl 邮件队列
【发布时间】:2014-10-26 10:28:37
【问题描述】:

我创建了一个邮件队列脚本,用于检查 MySQL 表中是否有过去有时间戳要发送的邮件。有时,“邮件队列”中的邮件可能比我想一次性发送的多,所以我使用线程分批发送邮件。有时当没有多少邮件要发送时,系统运行稳定,但有时这个始终运行的邮件队列进程(通过每 30 秒调用一次的 bash 脚本)被杀死,因为系统内存不足。我想防止“邮件队列”内存不足。

我想请你们中的任何人看看我下面的代码,也许我做错了什么。

提前致谢。

〜$ htop PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ 命令 5675 根 20 0 969M 528M 3744 S 0.0 14.1 2:33.05 perl /path/mailqueue.pl 服务器规格:1 个 vCPU; 3.75 Gib 内存;
sub start_threads {

    my @threads;

    # records found
    my $found = 0;

    my $sth = $dbh->prepare("SELECT mail_queue_id, project_id, type, name, email FROM mail_queue WHERE timestamp < NOW() AND active = 1 ORDER BY timestamp ASC LIMIT 10");
    $sth->execute();

    while (my $ref = $sth->fetchrow_hashref()) {
        # set if records are found
        $found = 1;

        # set email variables 
        my $id = $ref->{'mail_queue_id'};
        my $project_id = $ref->{'project_id'};
        my $type = $ref->{'type'};
        my $name = $ref->{'name'};
        my $email = $ref->{'email'};

        # create array with data
        my @select_arr = ($id, $project_id, $type, $name, $email);

        # start thread to send mail
        my $t = threads->new(\&sendmail, @select_arr);
        push(@threads,$t);
    }

    foreach (@threads) {
        # mail_queue_id
        my $id = $_->join;

        print "set email $id in queue inactive\n";  

        # set mail_queue record inactive -> MYSQL(event) mailqueue cleanup every 10 minutes
        my $sth = $dbh->prepare("UPDATE mail_queue SET active = 0 WHERE mail_queue_id = ? ");
        $sth->execute($id);
    }

    if($found eq 1) { # return rows in mail_queue < 1

        sleep(10);
        &start_threads;
    }
    else { # skip thread, wait 1 minut = sleep(1) to select new rows;
        sleep(30);
        &start_threads;
    }
}

# prepare send e-mail
sub sendmail {
    my @select_arr = @_; 

    # queue variables
    my $id = $select_arr[0];
    my $project_id = $select_arr[1];
    my $type = $select_arr[3];
    my $name = $select_arr[4];
    my $email = $select_arr[5];

    print "started sending email " . $id . " \n";

    # call function which sends the mail out 
    my $send = &email(@select_arr);

    # if mail is sent
    if($send eq 1) {
        print "done with sending email " . $id . "\n";

        sleep (1);

        # return unique id
        return $id;
    }
}

&start_threads;

【问题讨论】:

    标签: mysql multithreading bash perl email


    【解决方案1】:

    您正在做的事情可能非常昂贵 - 当 perl 线程创建您的进程的副本时 - 包括导入的模块、数据状态等。如果您的表返回很多行,您将很快耗尽内存。

    您可以通过ps -efT 看到这一点。

    对于你正在做的事情,你做这件事的方式是个坏主意。我会建议两种选择:

    • 坚持使用线程,开始一个固定的数字(比如 10)并使用Thread::Queue 序列化您的数据。这限制了进程副本的数量和线程启动开销。

    • 切换到使用fork()Parallel::ForkManager 将在这里或多或少地做你想要的。 fork() 是一种更有效的进程克隆方式 - 它只会按需复制内存,这意味着您的子进程要小得多。

    我将提供一些我在之前的答案中给出的示例: Perl daemonize with child daemons

    【讨论】:

    • 感谢您的快速回复。很抱歉,但我检查了我的代码,我以为我每 30 秒使用 bash 脚本调用一次该脚本,但事实并非如此。当我不通过 bash 脚本调用时,您的回答是否也会响应?因为那我没有复制整个过程?
    • 那么没那么糟糕 - 我假设 LIMIT 一次将其限制为 10 个结果?如果是这样,那么您可能的内存耗尽可能是 @threads 列表 - 因为它只会随着时间的推移而增长(您向其中添加项目,但从不删除它们)。您可能会发现 threads -&gt; list() 是一种更好的连接方式。
    猜你喜欢
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多