[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Рассылка большого количесва писем.
alex4715
Добрый день.

Получил от работодателя задачу:
Цитата

Имеется огромный список подписчиков, по которому надо сделать рассылку. 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);


P.S. В принципе я вижу слабые места, но у меня чувство что мой вариант, не особо верен, хотел бы услышать ваши мнения.



Спустя 1 час, 15 минут, 8 секунд (11.06.2012 - 12:19) vagrand написал(а):
Запускай скрипт по крону раз в 1 минуту и время его выполнения ограничивай 50 секундами. Ну и как ты и писал ставь пометку на адресах, для которых письма уже разосланы.

Спустя 14 минут, 5 секунд (11.06.2012 - 12:33) alex4715 написал(а):
Цитата (vagrand @ 11.06.2012 - 09:19)
Запускай скрипт по крону раз в 1 минуту и время его выполнения ограничивай 50 секундами. Ну и как ты и писал ставь пометку на адресах, для которых письма уже разосланы.

Крон можно использовать только для старта скрипта, а так он сам обновляет страницу и продолжает с места где остановился. Можно даже без крона это реализовать просто через переход. Базы емейлов я конечно не имею, да и спамить не хочу, тестил скрипт тоже на время затратой операции такой как добавление этих же емейлов в такую же таблицу, в результате я получаю идентичные таблицы размером 10 тысяч записей. Я вот ещё думал слип влипить на секунд 10 или минуту, чтобы не посчитали меня спаммером.
А вообще я хочу узнать правилен мой подход.

Спустя 3 часа, 8 минут, 23 секунды (11.06.2012 - 15:41) vagrand написал(а):
Цитата
Крон можно использовать только для старта скрипта, а так он сам обновляет страницу и продолжает с места где остановился.


Во-первых, крон работает с shell скриптами и никаких страниц в этом случае нет.
Во-вторых, если у вас max_execution_time=1 минуте, то полюбому скрипт будет прерываться, вопрос только в том будет он прерываться на середине обработки очередного письма или в отведенном вами месте.
В-третьих, подход с обновлением страницы убог, т.к. страница всегда должна быть открыта в браузере и по сути ваша рассылка зависит от того, есть ли у вас интернет и не заглючила ли у вас ОС на рабочем компе.

В любом случае прежде чем отвергать какую-то идею советую вникнуть в нее основательно.

Спустя 3 часа, 53 минуты, 7 секунд (11.06.2012 - 19:34) alex4715 написал(а):
Цитата (vagrand @ 11.06.2012 - 12:41)
Цитата
Крон можно использовать только для старта скрипта, а так он сам обновляет страницу и продолжает с места где остановился.


Во-первых, крон работает с shell скриптами и никаких страниц в этом случае нет.
Во-вторых, если у вас max_execution_time=1 минуте, то полюбому скрипт будет прерываться, вопрос только в том будет он прерываться на середине обработки очередного письма или в отведенном вами месте.
В-третьих, подход с обновлением страницы убог, т.к. страница всегда должна быть открыта в браузере и по сути ваша рассылка зависит от того, есть ли у вас интернет и не заглючила ли у вас ОС на рабочем компе.

В любом случае прежде чем отвергать какую-то идею советую вникнуть в нее основательно.

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

Спустя 20 минут, 10 секунд (11.06.2012 - 19:54) vagrand написал(а):
Цитата
В-третьих тут вы правы, но я не думаю что это критично при 10к емайлов ибо выполнится, в крайнем случаи тем же кроном я могу обратится по ссылке на скрипт.


Ваш вариант концептуально не верен. Критично для вас уже тогда, когда скрипт выполняется больше минуты. Что же касательно дерганья кроном урла, то тут есть 2-а варианта:
1. Если скрипту нужна авторизация, то надо просто дернуть его из крона не выйдет, а насколько это будет сложно уже зависит от реализации авторизации;
2. Если авторизация не нужна, то что помешает любому другому вызвать ваш скрипт?

Спустя 2 часа, 45 минут, 40 секунд (11.06.2012 - 22:40) FatCat написал(а):
Для запуска из браузера:
$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();

Спустя 6 часов, 28 минут, 57 секунд (12.06.2012 - 05:09) alex4715 написал(а):
Цитата (vagrand @ 11.06.2012 - 16:54)
Цитата
В-третьих тут вы правы, но я не думаю что это критично при 10к емайлов ибо выполнится, в крайнем случаи тем же кроном я могу обратится по ссылке на скрипт.


Ваш вариант концептуально не верен. Критично для вас уже тогда, когда скрипт выполняется больше минуты. Что же касательно дерганья кроном урла, то тут есть 2-а варианта:
1. Если скрипту нужна авторизация, то надо просто дернуть его из крона не выйдет, а насколько это будет сложно уже зависит от реализации авторизации;
2. Если авторизация не нужна, то что помешает любому другому вызвать ваш скрипт?

Да я пожалуй соглашусь, что подход через браузер критичен, я как то даже не подумал, такая задача встала в первые.
А насчёт перейти кроном по ссылке можно придумать такой костыль для аутенфикации: проверка айпишника, и что нибудь в посте интересное.

Спустя 6 дней, 9 часов, 31 минута, 23 секунды (18.06.2012 - 14:40) VELIK505 написал(а):
Смотря на сколько огромное?
Я рассылал по 10 000 писем за несколько минут через smtp и ничего не падало.

Спустя 16 дней, 16 часов, 7 минут, 15 секунд (5.07.2012 - 06:48) kamanch написал(а):
Из личного опыта с огромной базой подписчиков, да еще и письма с вложениями:
первый раз извратился так же - скрипт, рефреш. По непонятным причинам часть писем уходило по 2 раза на один адрес, что вызывало недовольство подписчиков. Помимо этого, иногда скрипт вообще вис. Приходилось его "подпинывать".

Извращение 2: скрипт отправлял одно письмо -> переадресация на другой домен -> переадресация на скрип рассылки. После того, как база подписчиков перевалила за определенное большое число, время рассылки составляло неприличные несколько суток, что не приемлимо.

Изящное решение было: старенький ком с серверной убунтой + postfix + cron
Но нехорошие подписчики регистрируют кривые мейлы, либо их ящики перестают быть активными, и встает задача мониторинга отлупов от их почтовых серверов, с последующим удалением невалидных адресов.

Я бы это сделал... обязательно... если бы любил пингвинов smile.gif Но, шеф (не без моей подачи) оказался мудрым, мы посчитали мои трудозатраты, перевели их в деньги, и выяснили, что купить за 70$ программу почтовой рассылки с мощным функционалом, мониторингом и прочими репортами будет на порядок дешевле. smile.gif

Спустя 1 месяц, 9 дней, 14 часов, 13 минут, 11 секунд (14.08.2012 - 21:01) alex4715 написал(а):
Цитата (kamanch @ 5.07.2012 - 04:48)
Из личного опыта с огромной базой подписчиков, да еще и письма с вложениями:
первый раз извратился так же - скрипт, рефреш. По непонятным причинам часть писем уходило по 2 раза на один адрес, что вызывало недовольство подписчиков. Помимо этого, иногда скрипт вообще вис. Приходилось его "подпинывать".

Извращение 2: скрипт отправлял одно письмо -> переадресация на другой домен -> переадресация на скрип рассылки. После того, как база подписчиков перевалила за определенное большое число, время рассылки составляло неприличные несколько суток, что не приемлимо.

Изящное решение было: старенький ком с серверной убунтой + postfix + cron
Но нехорошие подписчики регистрируют кривые мейлы, либо их ящики перестают быть активными, и встает задача мониторинга отлупов от их почтовых серверов, с последующим удалением невалидных адресов.

Я бы это сделал... обязательно... если бы любил пингвинов smile.gif Но, шеф (не без моей подачи) оказался мудрым, мы посчитали мои трудозатраты, перевели их в деньги, и выяснили, что купить за 70$ программу почтовой рассылки с мощным функционалом, мониторингом и прочими репортами будет на порядок дешевле. smile.gif

Да возможно не стоит писать велосипеды, а тупо брать готовые решения, но это не хардкор, это просто. Куда интересней найти бесплатное решение и допилить его под свои нужды к примеру свифт.

Спустя 23 минуты, 26 секунд (14.08.2012 - 21:24) killer8080 написал(а):
Цитата (alex4715 @ 11.06.2012 - 12:04)
Получил от работодателя задачу:Цитата

Имеется огромный список подписчиков, по которому надо сделать рассылку. Timeout и max_execution_time равны минуте. Что очень мало. Как вы поступите?


alex4715 задача явно была не практическая, а как тест на сообразительность. Такие скрипты запускаются только в CLI, и никакого ограничения времени выполнения там нет.

Спустя 3 часа, 38 минут, 49 секунд (15.08.2012 - 01:03) Nikitian написал(а):
killer8080
С почтой спешка не нужна. В одно время экспериментировал, когда надо было сделать рассылку по 3к получателям. В итоге наиболее удачный вариант был рассылать по 5 писем в минуту. Чаще - летели в спам. Рассылал через smtp по одному письму. Если использовать несколько обратных адресов и несколько ip для интерфейсов, то скорость можно увеличивать. Да, smtp был сторонний - яндекса.

Спустя 1 час, 5 минут, 20 секунд (15.08.2012 - 02:08) inpost написал(а):
alex4715
Для масс рассылок неплохо было бы заглянуть на php.net и почитать мануал про функцию mail() и вычитать, что для не стоит использовать функцию mail();
Кроме этого неплохо было бы указывать в заголовках, что идёт рассылка. Кроме этого ссылку о возможности отписаться от рассылки.
В спам-лист не попадёшь при стандартной рассылке по подписчикам, кроме этого всё 3к отправятся за 1 минуту. smile.gif

Спустя 2 часа, 35 минут, 1 секунда (15.08.2012 - 04:43) alex4715 написал(а):
inpost
Ок. Только вот проблема как сказали выше это было на логику, по этому я ограничиваюсь комментариями, а не полноценной рассылкой. Всё же намного проще я осознал свою беду написав рассылку по крону и через swift mailer. Работает на ура отправляет около 100 писем за минуту все довольны.

killer8080
Да вы правы. Меня как раз таки интересовал ограничение на консольный пшп скрипт, спасибо за телепатию и ваш ответ(без сарказма). Ну что я могу сказать алгоритм мой рабочий, реализация подвела так как я с кроном не был знаком, но теперь я исправился и признал свои ошибки.
Спасибо всем собравшимся.

Можем продолжить дискусию чутка в другом направлении.

У свифта есть такая фишка как коллективная рассылка. т.е. 1 письмо идёт 1000 пользователям как я понял за один раз. Т.е. есть 1 письмо и 1000 ссылок у каждого пользователя. Не знаю может нневерно трактую, с англ весьма печально. Но хотелось бы узнать как оно это делает.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2025 Invision Power Services, Inc.