Обработка больших объемов данных на PHP без тайм-аута браузера
У меня есть массив мобильных номеров, около 50 000. Я пытаюсь обрабатывать и отправлять массовые SMS на эти номера, используя сторонний API, но браузер замерзает в течение нескольких минут. Я ищу лучший вариант.
Обработка данных включает проверку типа мобильного номера (например, CDMA), назначение уникальных идентификаторов всем номерам для дальнейшей ссылки, проверка уникальных сборов за сеть/страну и т.д.
Я думал о очередности данных в базе данных и использовании cron для отправки около 5 тыс. партиями каждую минуту, но это займет время, если сообщений много. Каковы мои другие варианты?
Я использую Codeigniter 2 на сервере XAMPP.
Ответы
Ответ 1
Я бы написал два сценария:
Файл index.php
:
<iframe src="job.php" frameborder="0" scrolling="no" width="1" height="1"></iframe>
<script type="text/javascript">
function progress(percent){
document.getElementById('done').innerHTML=percent+'%';
}
</script><div id="done">0%</div>
Файл job.php
:
set_time_limit(0); // ignore php timeout
ignore_user_abort(true); // keep on going even if user pulls the plug*
while(ob_get_level())ob_end_clean(); // remove output buffers
ob_implicit_flush(true); // output stuff directly
// * This absolutely depends on whether you want the user to stop the process
// or not. For example: You might create a stop button in index.php like so:
// <a href="javascript:window.frames[0].location='';">Stop!</a>
// <a href="javascript:window.frames[0].location='job.php';">Start</a>
// But of course, you will need that line of code commented out for this feature to work.
function progress($percent){
echo '<script type="text/javascript">parent.progress('.$percent.');</script>';
}
$total=count($mobiles);
echo '<!DOCTYPE html><html><head></head><body>'; // webkit hotfix
foreach($mobiles as $i=>$mobile){
// send sms
progress($i/$total*100);
}
progress(100);
echo '</body></html>'; // webkit hotfix
Ответ 2
Я предполагаю, что эти числа находятся в базе данных, поэтому вам следует добавить новый столбец под названием isSent (или как вам нравится).
Этот следующий абзац, который вы набрали, должен быть поставлен в очередь и, возможно, сделан ночью/еженедельно/в случае необходимости. Если у вас нет определенной причины, это не должно делаться навалом по требованию. Вы даже можете добавить столбец в db, чтобы увидеть, когда он был последний проверен, чтобы, если число не было проверено, по крайней мере, за X дней, вы можете выполнить проверку этого числа по требованию.
Обработка данных включает проверку типа мобильного номера (например, CDMA), назначение уникальных идентификаторов всем номерам для дальнейшей ссылки, проверка уникальных сборов за сеть/страну и т.д.
Но это все еще возвращает вас к тому же вопросу о том, как это сделать для 50 000 номеров одновременно. Поскольку вы упомянули задания cron, я предполагаю, что у вас есть SSH-доступ к вашему серверу, что означает, что вам не нужен браузер. Эти задания cron могут выполняться через командную строку как таковую:
/usr/bin/php/home/username/example.com/myscript.php
Моя рекомендация состоит в том, чтобы обрабатывать 1000 номеров за раз каждые 10 минут через cron и время, сколько времени потребуется, а затем сохранить его в БД. Поскольку вы используете задание cron, похоже, что это не чувствительные к времени SMS-сообщения, поэтому они могут быть распространены. Как только вы узнаете, сколько времени понадобилось для этого script для запуска 50 раз (50 * 1000 = 50k), вы можете обновить свое задание cron, чтобы работать чаще/реже.
$time_start = microtime(true);
set_time_limit(0);
function doSendSMS($phoneNum, $msg, $blah);
$time_end = microtime(true);
$time = $time_end - $time_start;
saveTimeRequiredToSendMessagesInDB($time);
Кроме того, возможно, вы заметили set_time_limit (0), это скажет PHP не истекать тайм-аут после 30 секунд. Если вы можете изменить файл PHP.ini, вам не нужно вводить эту строку кода. Даже если вы можете редактировать файл PHP.ini, я по-прежнему рекомендую не изменять эту функцию, так как вам могут потребоваться другие страницы.
http://php.net/manual/en/function.set-time-limit.php
Ответ 3
Если это не одноразовая ситуация, подумайте о разработке лучшего решения.
То, что вы в основном хотите, это очередь, которую может обрабатывать связанный с браузером процесс, и рабочие процессы 1-N могут читать и обновлять.
Помещение работы в очередь должно быть довольно недорогим - возможно, множество простых инструкций INSERT для SQL RDBMS.
Затем у вас может быть демона или два (или 100, распределенных на нескольких серверах), которые считываются из очереди и обрабатываются. Вы захотите быть осторожными здесь и избегать двух рабочих, принимающих одну и ту же задачу, но которые не сложно скопировать.
Итак, ваш рабочий процесс, связанный с браузером: нажмите кнопку, которая заставляет кучу вещей быть добавленными в очередь, а затем перенаправляет на какой-то интерфейс "статус очереди", где пользователь может наблюдать за системой, пережевывая всю свою работу.
Система, подобная этому, хороша, потому что ее легко масштабировать по горизонтали достаточно.
EDIT: ответ Christian Sciberras идет в этом направлении, за исключением того, что браузер заканчивает движение с обеих сторон (он добавляет в очередь, затем управляет рабочим процессом)
Ответ 4
Cronjob будет вашим лучшим выбором, я не понимаю, почему это займет больше времени, чем делать это в браузере, если ваша единственная проблема на данный момент - время выхода из браузера.
Если вы настаиваете на том, чтобы делать это через браузер, то другое решение будет делать это в партиях, скажем 1000 и перенаправляясь на тот же script, но с некоторой ссылкой на то, где он встал до последнего времени в переменной $_GET.