我创建了一个mailqueue脚本,用于检查MySQL表中是否有过去发送时间戳的邮件。有时可能是'mailqueue'中的邮件数量超过了我想要一次发送的邮件数量,因此我使用线程批量发送邮件。有时当没有太多邮件要发送时系统运行稳定,但有时候这个始终运行的邮件队列进程(通过bash脚本每30秒调用一次)就会被杀死,因为系统内存不足。我想防止“邮件队列”耗尽内存。
我想请你们中的任何人看看下面的代码,也许我做错了什么。
提前致谢。
~$ htop PID USER PRI NI VIRT RES SHR S CPU% MEM% TIME+ Command 5675 root 20 0 969M 528M 3744 S 0.0 14.1 2:33.05 perl /path/mailqueue.pl Server specs: 1 vCPU; 3.75 Gib Memory;
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;
答案 0 :(得分:1)
你正在做的事情可能相当昂贵 - 当perl线程创建你的进程副本时 - 包括导入的模块,数据状态等。如果你的表返回很多行,你将很快耗尽内存。
你可以通过ps -efT
来看到这一点。
对于你正在做的事情,你做这件事的方式是个坏主意。我建议两种选择:
坚持使用线程,启动固定数字(例如10)并使用Thread::Queue
序列化您的数据。这限制了进程副本的数量和线程启动开销。
切换为使用fork()
。 Parallel::ForkManager
会在这里做或多或少的事情。 fork()
是一种更有效的进程克隆方法 - 它只会按需复制内存,这意味着您的子进程要小得多。
我将在之前的回答中提供一些例子: Perl daemonize with child daemons