За последние 24 часа нас посетили 21467 программистов и 1018 роботов. Сейчас ищут 744 программиста ...

Проблема с рассылкой

Тема в разделе "PHP для новичков", создана пользователем 007, 20 фев 2018.

  1. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    На просторах инета нашел скрипт рассылки. Скрипт использует класс PHPMailer. Чуть переменил содержание и скрипт стал брать файлы из базы MYSQL, а после отправки записывать в базу флаг об отправке и дату с временем отправки. Пока тестил все было ничего. Сегодня залил на сайт и запустил исполняемый файл. Отправилось писем 40 и ошибка по валидности (в имени почты "ru" не дописали). Исправляю, запускаю по новой опять отправилось писем 30-40 и ошибка повторилась. Ну и та несколько раз. И вдруг при очередном запуске скрипт виснет т.е. страница к которой обращается браузер для запуска скрипта висит в постоянном обновлении. Думаю - ошибок не нашел, тихо тянет остальные адреса (в базе их под 1000), тем более есть пауза между отправлениями. Так продолжалось несколько минут и я понял что скрипт повис. Дернул базу - точно ничего не пишется. Закрываю страницу захожу на хостинг вырубаю ссылки внутри файла. Время 9:45. Захожу на SMTP-сервис, вижу что отправилось 6к писем. Поехал ребенка из школы забрать. Прошло пару часов. Звонят клиенты - вы нас спамите. Бегу за комп в базе запись оборвалась на строке 246 (ошибок нет) сделанные записи в базе в 11:45. На SMTP-сервисе ушло 40к писем. Писец. Что это за хрень?
    Хостинг у меня 000webhost. Я новичок. Скрипт на PHP если нужно выложу.
     
  2. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.410
    Симпатии:
    1.768
    потенциально говно

    хорошо

    потенциально говно

    завоняло

    хорошо. ещё можно сделать поле "отправляется", чтобы если таки не отправилось - переотправлять.

    локально? на каких имейлах?

    судя по терминологии, ты мало знаком с PHP

    не ломай цикл при ошибке и всё

    что ты имеешь в виду?

    тренироваться надо на кошках, просто, а не на живых людях

    ну выкладывай
     
    Deonis нравится это.
  3. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    Ну не пинайте плиз и так тошно. На спеца денег нет учусь на старости лет.
    Это то что "исполняемый файл" т.е. запускаю его
    PHP:
    1. <?php
    2. require "sendemail_to_all.php"; // подключили нашу функцию
    3. $servername = "localhost";
    4. $username  = "username";
    5. $password  = "password";
    6. $database  = "base";
    7.  
    8. // Create connection
    9. $db = mysqli_connect($servername, $username, $password, $database);
    10. if (!$db) {
    11.    echo "Ошибка: Невозможно установить соединение с MySQL.";
    12.    echo "Код ошибки errno: " . mysqli_connect_errno();
    13.    echo "Текст ошибки error: " . mysqli_connect_error();
    14. }
    15. $result = mysqli_query($db, "SELECT `ID`, `IPFirstname`, `IPEmail` FROM `table` WHERE `send`=1"); // сделали выборку из базы
    16. if (mysqli_num_rows($result)) { // если результат больше 0
    17.    while ($arrs = mysqli_fetch_assoc($result)) { // запускаем цикл
    18.        $data[] = $arrs; // и формируем массив с данными пользователей
    19.        
    20.    }
    21.    $sending = sendemail_to_all("мое мыло", "мое имя", "тема письма", $message, $data); // отправляем письмо всем и записываем результат в переменную в виде массива, который отдаст функция
    22. } else { //иначе, если пользователей 0
    23.    $sending = "<div style='text-align:center'>База пуста!</div>"; // задаем переменную с сообщением
    24. }
    25. $onw = "";
    26. $c  = 0;
    27. if (is_array($sending)) { // если переменная является массивом
    28.    foreach ($sending as $wmess) { // проходим циклом
    29.        $m = explode(" | ", $wmess); // разбиваем строку по разделителю, превращая в массив, чтобы удобнее обращаться к элементам
    30.        $onw .= "<div>" . ((strpos($m[0], "isuccess") !== false) ? "<span style='color:green'>Отправлено: </span>" : "<span style='color:red'>Ошибка: </span>") . " ID: " . $m[1] . " Email: " . $m[2] . " Время: " . $m[3] . "</div>";
    31.        $c++;
    32.        //Помечаем кому отправлено. для этого в базе ставим флаг "2" в строке ID//
    33.        $t    = $m[3]; // Дата и время
    34.        $ok    = $m[1]; //ID
    35.        $uh    = "UPDATE table SET send=2, time='$t' WHERE id=$ok";
    36.        $reslt = mysqli_query($db, $uh); // пишем в базу флаг, кому отправлено
    37.    }
    38.    $all = $c; // счетчик
    39. } else {
    40.    $onw .= $sending;
    41.    $all = 0;
    42. }
    43. echo $onw; // выводим результат
    44. echo "Всего отправлено: " . $all;
    К нему подключается файл с настройками PHPMailer

    PHP:
    1. <?php
    2.  
    3. function sendemail_to_all($fromemail, $fromname, $subject, $message, $data)
    4. {
    5.    date_default_timezone_set("Europe/Astrakhan"); // временная зона
    6.    require 'class.phpmailer.php'; // подключили скрипт
    7.    require 'class.smtp.php'; // подключили скрипт
    8.    $mail = new PHPMailer(); // инициализация класса
    9.    file_exists("phpmailer.lang-ru.php") ? $mail->SetLanguage("ru", "/language/") : $mail->SetLanguage("en", "/language/"); // языковой пакет
    10.    $mail->isSMTP(); // указали, что работаем по протоколу смтп
    11.    $mail->Host      = "хост"; // хост
    12.    $mail->Port      = "465"; // порт
    13.    $mail->SMTPSecure = 'ssl'; //шифрование
    14.    $mail->SMTPAuth  = true; // аутентификация включена
    15.    $mail->Username  = "ящик"; // реальный ящик
    16.    $mail->Password  = "пароль"; // пароль от ящика
    17.    $mail->CharSet    = "UTF-8"; // кодировка
    18.    $mail->setFrom($fromemail, $fromname); // от кого
    19.    $mail->addReplyTo($fromemail, $fromname); // кому ответить
    20.    $mail->Subject = $subject; // тема письма (заголовок)
    21.    $mail->isHTML(true);
    22.  
    23.    $body = file_get_contents('само письмо.html');
    24.  
    25.    $mail->msgHTML($body); // формируем тело
    26.  
    27.    $i = 0;
    28.    foreach ($data as $row) { // проход по массиву из бд
    29.        unset($user); // вначале каждый раз очищаем переменную
    30.        $user = $row['ID']; // уникальный id пользователя
    31.        $mail->addCustomHeader("Precedence", "bulk"); // добавили заголовок что рассылка
    32.      
    33.        $mail->addAddress($row['IPEmail'], $row['IPFirstname']); // добавили адрес и имя получателя
    34.        $mail->Body = str_replace(array(
    35.            "{IPFirstname}",
    36.            "{ID}"
    37.        ), array(
    38.            $row['IPFirstname'],
    39.            $user
    40.        ), $body); // вставляем в сообщение имя пользователя и уникальный id для ссылки
    41.      
    42.        if (!$mail->send()) { // если письмо не отправлено
    43.            $return[] = "ierror | " . $row['IPFirstname'] . " | " . str_replace("@", "&#64", $row['IPEmail']) . " " . $mail->ErrorInfo . " | " . date("Y-m-d H:i:s"); // возвращаем массив с ошибкой
    44.            break; // останавливаем перебор массива
    45.        } else { // если отправка прошла удачно
    46.            $return[] = "isuccess | " . $row['ID'] . " | " . str_replace("@", "&#64", $row['IPEmail']) . " | " . date("Y-m-d H:i:s"); // возвращаем массив с отчетом
    47.            ($i % 2 == 0) ? sleep(0.2) : sleep(0.4); // делаем паузу при рассылке между сообщениями
    48.        }
    49.        $mail->clearAddresses(); // в цикле чистим адрес, чтобы при новом проходе добавить другой
    50.        $mail->clearCustomHeaders(); // чистим в цикле заголовки, чтобы при новом проходе добавить новые
    51.        $i++;
    52.    }
    53.    $mail->clearAttachments();
    54.    $mail->clearReplyTos(); // очищаем
    55.    return $return; // функция возвращает нам результат в массиве
    56. }
    57. ?>
     
    #3 007, 20 фев 2018
    Последнее редактирование модератором: 20 фев 2018
  4. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    На чем тестил - у меня на нетбуке (winXPsp3) установлен Open Sourse с модулями Apache-2.2 PHP-5.4 MySQL-5.5 (это не от жадности, а от нищеты. Нищебродство - главный бич современности)
    Есть адреса на mail.ru, ya.ru, gmail.com. Все это работало как часы в течении отладки т.е. дней 5 я долбился над работоспособностью всего этого добра. И заработало. Для тестов разослал сначала себе на 4 адреса, все пришло как надо. Потом клиентам на 180 адресов - тоже без проблем. Все записалось куда положено и лишнего при перезапуске скрипт не брал. На радостях загрузил на сервак и сегодня получил стыд и позор описанный в первом посте.
     
  5. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.410
    Симпатии:
    1.768
    ну вот ты тестил на локалке и в реальности ничего никому не слал
    и всё у тебя было хорошо

    а потом ты решил послать
    и оказалось, что просто капец

    поэтому тестировать надо сначала на локалке, потом тестировать на продакшене на маленьком количестве, аккуратно и с бэкапами.

    а потом только "ушел за ребёнком" и оставил без пресмотра :D

    бэкапы делай каждый раз, а то ты с таким подходом можешь нарваться на ситуацию
    --- Добавлено ---
    sleep не слипает на полсекунды. см доку.
    --- Добавлено ---
    хз че не так, может clearaddresses не отрабатывает как надо

    как часто ты запускаешь этот скрипт?
     
  6. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    оно конечно спасибо, но я из локалки слал на реальные адреса и письма приходили в том виде в котором я задумывал. Что есть в "реальности"? И еще, я сейчас получил от SMTP логи, изучу добавлю. Но мне кажется все работало до какого-то момента. Критику принял. Не понимаю только что бэкапить. Файлов то целых два. Версии с которыми не работало удаляю. С которыми работало оставляю. Таких у меня сейчас три.
    Как запускаю - база под 1000, запускаю первый файл,данные выбираются в массив, далее обрабатываются по порядку, когда скрипт находит ошибку или завершается список рассылки об этом выводится сообщение. Сегодня было так запустил скрипт он начал выполняться и остановился на ошибке нет ни одного адреса (или что то вроде). Дернул базу - скрипт отработал корректно, записи в поле базы поменялись на "2" дата время отсылки обозначилась. Остальные как были так и есть с "1" т.е. готовые для условий скрипта. Прошло 2 минуты, исправил окончание адреса, дописал ru. Скрипт начал работу с этого места и до следующей ошибки в имени адреса. Опять поправил все повторилось. И потом еще несколько раз. А потом завис и капец котенку - срать не будет.
    Если вопрос "как часто" общий - то почти ежедневно юзаю.
     
    #6 007, 20 фев 2018
    Последнее редактирование: 20 фев 2018
  7. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    Что то все печальней и печальней. Отчет SMTP показывает, что только первая серия рассылки отдала в сеть по одному письму. Вторая, третья и четвертая выдали у же по два письма каждому. А когда скрипт завис (последняя серия) , пошло 99 и до 299 писем каждому. Вот это так насыпал. Как выполняется скрипт на сервере? Расскажите подробно.
    Что я делал - файл с первым скриптом закинул в корень сайта. Пусть файл будет 1.php второй файл (в котором настройки) лежал в другой директории. Запускал файл 1.php путем перехода по адресу "мой.сайт/1.php" в браузере. Дальше шло выполнение и на странице браузера выводился отчет. Когда страница в браузере зависла, я ее закрыл и зашел в корень сайта и в файле 1.php изменил пути ко втором файлу. А нужно было тупо закоментить пароль во втором файле (ИМХО конечно, я же начинающий, и плюс к дочке спешил). Видимо это и стало ошибкой. Второй файл считав в массив данные из 1.php не смог их вернуть в 1. php, и гонял по кругу. Симптом - рукожоп. Ладно, это я вроде понял, но тогда вопрос - как защитить себя от подобно казуса впредь (думаю написать там где нибудь DIE) и почему в начале отсылалось по два письма? Как это победить? Наверное контролить во втором файле саму отсылку, но как? Там же есть после прохода цикла очистка addadress, но она не сработала когда нужно было.
     
  8. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.410
    Симпатии:
    1.768
    я не понял, но рад, что ты разобрался :D
     
  9. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    Это примерно))) Я не могу логи получть у хостера. Саппорт только на платном аккаунте. Сейчас на форум к ним писать буду. Нужно понять что загнало скрипт в цикл. Не предположить , а точно выяснить. Консоль мне недоступна. Если бы можно было скриптом логи у них добыть. Эх ладно.
     
  10. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    Хостер логов так и не дал. Но вот что удалось выяснить. При работе из консоли скрипт отправляет письма ка положено, ровно столько сколько пишу в условии. Выловились косяки. Пока все не отправит - в базу не пишет и база отваливается.
    Код (Text):
    1. > php newmail.php                                                                                                            
    2.                                                                                                                              
    3. Warning: mysqli_query(): MySQL server has gone away in D:\OSPanel\domains\MAIL.loc\newmail.php on line 40                                                                                                                                                
    4. Warning: mysqli_query(): Error reading result set's header in D:\OSPanel\domains\MAIL.loc\newmail.php on line 40                                                                                                                                          
    5. Fatal error: Uncaught exception 'mysqli_sql_exception' with message 'MySQL server has gone away' in D:\OSPanel\domains\MAIL.loc\
    6. newmail.php:40                                                                                                                
    7. Stack trace:                                                                                                                  
    8. #0 D:\OSPanel\domains\MAIL.loc\newmail.php(40): mysqli_query(Object(mysqli), 'UPDATE `new_t` ...')                            
    9. #1 {main}                                                                                                                    
    10.   thrown in D:\OSPanel\domains\MAIL.loc\newmail.php on line 40                                                                  
    при чем складывается база только если писем больше 19 (с локальной машины тестил) и скрипт перестает работать (рассылка прекращаетя). На сервере вообще ставил по крону раз в 10 мин 300 писем уходит. Можно и больше поставить и уйдет.Только в базу же не пишется и при следующем запуске опять тоже кино (с начала базы выбирает). В ходе тестов выявилось, что при запуске скрипта из браузера, письмо может отправиться несколько раз одному и тому же адресу. При запуске из консоли и кроном таких косяков не обнаружилось. Жаль консоль на сервере недоступна. Может попробовать базу переподключать как-то?
     
    #10 007, 22 фев 2018
    Последнее редактирование: 22 фев 2018
  11. _ne_scaju_

    _ne_scaju_ Старожил

    С нами с:
    25 ноя 2016
    Сообщения:
    2.149
    Симпатии:
    118
    Значит у тебя где отправка происходит на e-mail не правильно, может запрос составлен не правильно.
     
  12. keren

    keren Новичок

    С нами с:
    15 ноя 2017
    Сообщения:
    513
    Симпатии:
    42
    Не, повторная отправка я думаю там получается так - скрипт взял из базы со статусом неотправленно например 10 писем, отправил, он должен затем поменять их статус на отправленно, но база отваливается, письма отправлены но статус получателей не изменился, и адреса снова берутся из базы для отправки.
     
  13. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.410
    Симпатии:
    1.768
  14. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    еще mailchimp, sendgrid последний самый простой и лояльный. Я сейчас с ними. После того как вышла эта засада, они мне подарили 20к писем для бесплатной отправки, оперативно предоставили логи постоянно пытались помочь в решении. Короче, контора - огонь. Sendgrid - наше всё.
    так и есть. Нужно посмотреть таймауты и попытаться выставить их в htaccess. Т.к. php.ini мне недоступен для редактирования. Для поддержки соединения смотрю в сторону mysqli_ping. Будет что-то типа
    Код (Text):
    1. if (! mysqli_ping, $db) mysqli_connect...
    Эксперименты продолжаются)))
     
  15. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    Промежуточные итоги: применил
    Код (Text):
    1. mysqli_close($db);
    2. if( !mysql_ping($db) ) $db = mysqli_connect($servername, $username, $password, $database, true);
    Идея состоит в том,что объем данных из базы не уходит весь сразу, а пересылается с задержкой (еще по sleep остались вопросы). И когда часть сообщений остается не отправленными база уходит по таймауту. Т.к. хостеры не особо хотят менять таймаут, а я сам его установить не могу(нет прав), то принял решение отключать после запроса и снова включать когда нужно писать флаги в базу. В принципе, наверное, можно и без пинга обойтись, я же не жду пока отвалится, а сам закрываю. Заработало, шлет 300 писем за 300 секунд и пишет в базу. Можно наверное и больше. Вылезла непонятная особенность - из крона пишет что-то типа "тело письма пустое", из браузера все о.к. Х.з. что это за рога. Проверил трижды. Менял только способ запуска.
     
  16. 007

    007 Новичок

    С нами с:
    20 фев 2018
    Сообщения:
    10
    Симпатии:
    0
    Все печально. На малых объемах отсылает столько сколько задаю. Но если планку поднять, рассылка происходит по непонятным правилам. Я выяснил, что PHPMailer, отправляет столько писем, сколько ему приходит. из скрипта. Из базы выбирается ровно то, что я запрашиваю. Но что в массиве - это жуть. При чем количество отсылаемых писем рандомно. Что это за хрень и как ее победить? Пишу уже логи одновременно 3-х переменных, ничего понять не могу.