Добрый день.
Получил от работодателя задачу:
Цитата |
Имеется огромный список подписчиков, по которому надо сделать рассылку. Timeout и max_execution_time равны минуте. Что очень мало. Как вы поступите? |
Цитата |
У нас есть таблица email_list в ней 3 поля id, email, marker. Я даю на рассылку 50 секунд, после чего ставлю метку в поле marker = 1 по айдишнику, делаю рефреш. Ищу метку, если она есть, то начинаю с неё отсылать дальше. И так пока данный айди не будет равен количеству записей в бд, после чего очищаю метки и вывожу надпись об окончании. |
class Mailer {
protected $_db;
protected $_time_start;
protected $_content;
public function __construct($content) {
$this->_time_start = $this->microTimeInt();
$this->_content = $content;
$this->_db = new SQLiteDatabase('email_list.db');
}
//Возвращает округлённое время в секундах
public function microTimeInt(){
list($usec, $sec) = explode(" ", microtime());
return ((int)$usec + (int)$sec);
}
//Возвращает массив id и email больше или равное $id
public function getEmail($id = 1){
return $this->_db->arrayQuery('SELECT id, email FROM email_list WHERE id >= '.$id, SQLITE_ASSOC);
}
//Возвращает позицию маркера
public function getMarkerId(){
$res = $this->_db->arrayQuery('SELECT id FROM email_list WHERE marker = 1', SQLITE_ASSOC);
if (isset($res[0][id]))
return $res[0][id];
else
return 0;
}
//Устанавливаем маркер на позицию
public function takeMarker($id, $value = 1){
$this->_db->query('UPDATE email_list SET marker = '.$value.' WHERE id = '.$id);
return true;
}
//Возращает массив email который больше id маркера
public function getEmailMarker(){
$marker_id = $this->getMarkerId();
if (!$marker_id)
$email = $this->getEmail();
else
$email = $this->getEmail($marker_id);
return $email;
}
//Возвращает колличество полей в таблице
public function countEmail(){
$res = $this->_db->arrayQuery('SELECT count(id) FROM email_list', SQLITE_ASSOC);
return $res[0]['count(id)'];
}
//Оссуществляет весь процесс отправки по таймеру
public function sendTimer() {
$emails = $this->getEmailMarker();
$count_email = $this->countEmail();
foreach ($emails as $email) {
if($email['id'] != $count_email)
{
$time_end = $this->microTimeInt();
if ($time < 50){
$this->sendMails($email['email']);
}
else {
$this->takeMarker($email['id']);
die("<meta http-equiv='refresh' content='0';>"); //header("Refresh:0");
}
$time = $time_end - $this->_time_start;
}
else{
$this->takeMarker($this->getMarkerId(), 0); // or NULL
echo 'Рассылка выполнена';
break;
}
}
}
//Рассылка самого письма
public function sendMails($email){
//код рассылки
//$this->_content - массив контента для рассылки
return true;
}
}
$send_mail = new Mailer();
$send_mail->sendTimer($content);
Цитата (vagrand @ 11.06.2012 - 09:19) |
Запускай скрипт по крону раз в 1 минуту и время его выполнения ограничивай 50 секундами. Ну и как ты и писал ставь пометку на адресах, для которых письма уже разосланы. |
Цитата |
Крон можно использовать только для старта скрипта, а так он сам обновляет страницу и продолжает с места где остановился. |
Цитата (vagrand @ 11.06.2012 - 12:41) | ||
Во-первых, крон работает с shell скриптами и никаких страниц в этом случае нет. Во-вторых, если у вас max_execution_time=1 минуте, то полюбому скрипт будет прерываться, вопрос только в том будет он прерываться на середине обработки очередного письма или в отведенном вами месте. В-третьих, подход с обновлением страницы убог, т.к. страница всегда должна быть открыта в браузере и по сути ваша рассылка зависит от того, есть ли у вас интернет и не заглючила ли у вас ОС на рабочем компе. В любом случае прежде чем отвергать какую-то идею советую вникнуть в нее основательно. |
Цитата |
В-третьих тут вы правы, но я не думаю что это критично при 10к емайлов ибо выполнится, в крайнем случаи тем же кроном я могу обратится по ссылке на скрипт. |
$start_time = time();
while( ( (time() - $start_time) < 50 )
{
// рассылка писем;
}
header("Refresh: 0;url=http://site.ru/my_mailer.php");
echo("<html><head><meta http-equiv='refresh' content='0; url=http://site.ru/my_mailer.php'> </head><body></body></html>");
exit();
Цитата (vagrand @ 11.06.2012 - 16:54) | ||
Ваш вариант концептуально не верен. Критично для вас уже тогда, когда скрипт выполняется больше минуты. Что же касательно дерганья кроном урла, то тут есть 2-а варианта: 1. Если скрипту нужна авторизация, то надо просто дернуть его из крона не выйдет, а насколько это будет сложно уже зависит от реализации авторизации; 2. Если авторизация не нужна, то что помешает любому другому вызвать ваш скрипт? |
Цитата (kamanch @ 5.07.2012 - 04:48) |
Из личного опыта с огромной базой подписчиков, да еще и письма с вложениями: первый раз извратился так же - скрипт, рефреш. По непонятным причинам часть писем уходило по 2 раза на один адрес, что вызывало недовольство подписчиков. Помимо этого, иногда скрипт вообще вис. Приходилось его "подпинывать". Извращение 2: скрипт отправлял одно письмо -> переадресация на другой домен -> переадресация на скрип рассылки. После того, как база подписчиков перевалила за определенное большое число, время рассылки составляло неприличные несколько суток, что не приемлимо. Изящное решение было: старенький ком с серверной убунтой + postfix + cron Но нехорошие подписчики регистрируют кривые мейлы, либо их ящики перестают быть активными, и встает задача мониторинга отлупов от их почтовых серверов, с последующим удалением невалидных адресов. Я бы это сделал... обязательно... если бы любил пингвинов ![]() ![]() |
Цитата (alex4715 @ 11.06.2012 - 12:04) |
Получил от работодателя задачу:Цитата Имеется огромный список подписчиков, по которому надо сделать рассылку. Timeout и max_execution_time равны минуте. Что очень мало. Как вы поступите? |