#php #import #background-process
#php #импорт #фоновый процесс
Вопрос:
Подробные сведения
Когда пользователь впервые входит в мое приложение, мне нужно импортировать все товары их магазина из API, это может быть от 10 до 11 000 товаров. Поэтому я думаю, что мне нужно сообщить пользователю, что мы импортируем их продукты и отправим их по электронной почте, когда закончим.
Вопросы
Каков наилучший способ импортировать эти данные, не требуя от пользователя оставаться на странице?
Должен ли я пойти по этому pcntl_fork
пути?
Было бы system
лучше стилизовать фоновые задачи?
Ответ №1:
AFAIK нет способа pcntl_fork()
выполнить процесс с веб-сервера, вы можете сделать это только из командной строки. Однако вы можете запустить дочерний процесс, используя exec()
(или аналогичный), который продолжит выполняться после завершения.
Я не знаю, насколько это «правильно», но я бы сделал что-то вроде этого:
upload.php — Попросите пользователя загрузить список своих продуктов в любом формате, который вы хотите. Я предполагаю, что вы знаете, как это сделать, и не будете включать какой-либо код — если вам нужен пример, дайте мне знать.
store.php — форма загрузки отправляется в этот файл:
// Make sure a file was uploaded and you have the user's ID
if (!isset($_FILES['file'],$_POST['userId']))
exit('No file uploaded or bad user ID');
// Make sure the upload was successful
if ($_FILES['file']['error'])
exit('File uploaded with error code '.$_FILES['file']['error']);
// Generate a temp name and store the file for processing
$tmpname = microtime(TRUE).'.tmp';
$tmppath = '/tmp/'; // ...or wherever you want to temporarily store the file
if (!move_uploaded_file($_FILES['file']['tmp_name'],$tmppath.$tmpname))
exit('Could not store file for processing');
// Start an import process, then display a message to the user
// The ' > /dev/null amp;' is required here - it let's you start the process asynchronously
exec("php import.php "{$_POST['userId']}" "$tmppath$tmpname" > /dev/null amp;");
// On Windows you can do this to start an asynchronous process instead:
//$WshShell = new COM("WScript.Shell");
//$oExec = $WshShell->Run("php import.php "{$_POST['userId']}" "$tmppath$tmpname"", 0, false);
exit("I'm importing your data - I'll email you when I've done it");
import.php — обрабатывает импорт и отправляет электронное письмо
// Make sure the required command line arguments were passed and make sense
if (!isset($argv[1],$argv[2]) || !file_exists($argv[2])) {
// handle improper calls here
}
// Connect to DB here and get user details based on the username (passed in $argv[1])
// Do the import (pseudocode-ish)
$wasSuccessful = parse_import_data($argv[2]);
if ($wasSuccessful) {
// send the user an email
} else {
// handle import errors here
}
// Delete the file
unlink($argv[2]);
Основная проблема с этим подходом заключается в том, что если множество людей одновременно загружают списки для импорта, вы рискуете перегрузить свои системные ресурсы несколькими одновременными версиями import.php
запуска.
По этой причине, возможно, лучше запланировать задание cron для импорта списков по одному, как предложил Аарон Брюс, но какой подход лучше для вас, будет зависеть от ваших точных требований.
Ответ №2:
Я думаю, что «стандартным» способом сделать это в PHP было бы запускать cron каждые пять минут или около того, который проверяет очередь ожидающих импорта.
Итак, ваш пользователь входит в систему, частью процесса входа в систему является добавление их в вашу таблицу «pending_import» (или, как вы решите сохранить очередь импорта). Тогда при следующем запуске cron он позаботится о текущем содержимом вашей очереди.