2014-10-26 6 views
0

Я создал сценарий электронной почты, который проверяет таблицу MySQL для почты, у которой есть метка времени в прошлом для отправки. Иногда бывает, что в «mailqueue» больше почты, чем я хотел бы отправить за один раз, поэтому я использовал потоки для отправки почты партиями. Иногда, когда нет почты для отправки, система работает стабильно, но в других случаях этот процесс рассылки, который всегда работает (через сценарий bash , который вызывает его каждые 30 секунд ) убивается, поскольку в системе заканчивается память , Я бы хотел, чтобы «почта» не хватало памяти.Perl mailqueueing with threads 'out of memory'

Я хотел бы попросить кого-нибудь из вас взглянуть на мой код внизу, возможно, я что-то сделаю неправильно.

Заранее спасибо.

 
~$ 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; 

ответ

1

Что вы делаете это потенциально довольно дорого - когда Perl нити создает копию процесса - в том числе импортируемых модулей, состояний данных и т.д. Если ваша таблица возвращает много строк, вы очень быстро съедят память.

Вы можете увидеть это, выполнив ps -efT.

Что вы делаете, то, как вы это делаете, это плохая идея. Я хотел бы предложить два варианта:

  • палки с нитями, начните фиксированное число (скажем, 10) и использовать Thread::Queue сериализацию данных. Это ограничивает количество копий процесса и служебные данные запуска потока.

  • переключатель для использования fork(). Parallel::ForkManager будет делать более или менее то, что вы хотите здесь. fork() - это более эффективный способ клонирования процесса - он будет копировать только по требованию, то есть ваши подпроцессы намного меньше.

Я предложу некоторые примеры, которые я дал в предыдущем ответе: Perl daemonize with child daemons

+0

Спасибо за вас быстрый ответ. Извините, но я проверил свой код, и мне показалось, что я вызываю этот скрипт каждые 30 секунд с помощью скрипта bash, но это не так. Отвечает ли ваш ответ также, когда я не звоню через сценарий bash? Потому что тогда я не копирую весь процесс? –

+0

Не совсем так плохо, я полагаю, что 'LIMIT' ограничивает его до 10 результатов за раз? Если это так, то вероятным исчерпанием памяти может быть тот список '@ threads', который будет только увеличиваться с течением времени (вы добавляете к нему элементы, но не удаляете их). Вы можете найти 'threads -> list()' лучший способ сделать соединение. – Sobrique