Вот одно решение, не самое лучшее, но отлично подойдет для Linux:
Разделите обрабатывающий PHP на отдельные скрипты CLI, в которых:
- Входные данные командной строки включают в себя `$ id` и` $ item`
- Сценарий записывает свой PID в файл в `/ tmp / $ id. $ Item.pid`
- Скрипт выводит результаты в виде XML или чего-то, что можно прочитать в PHP на стандартный вывод
- По завершении скрипт удаляет файл `/ tmp / $ id. $ Item.pid`.
Ваш мастер-скрипт (предположительно на вашем веб-сервере) будет делать:
- `exec (" nohup php myprocessing.php $ id $ item> /tmp/$id.$item.xml ");` для каждого элемента
- Опрашивать файлы `/ tmp / $ id. $ Item.pid` до тех пор, пока все они не будут удалены (достаточно опроса / проверки сна)
- Если они никогда не удаляются, уничтожьте все сценарии обработки и сообщите об ошибке
- В случае успеха прочитайте из `/ tmp / $ id. $ Item.xml` для форматирования / вывода пользователю
- Удалите файлы XML, если вы не хотите кэшировать для последующего использования
Фоновое nohup
запущенное приложение будет работать независимо от скрипта, который его запустил.
Это меня заинтересовало настолько, что я решил написать POC.
test.php
<code><?php
$dir = realpath(dirname(__FILE__));
$start = time();
// Time in seconds after which we give up and kill everything
$timeout = 25;
// The unique identifier for the request
$id = uniqid();
// Our "items" which would be supplied by the user
$items = array("foo", "bar", "0xdeadbeef");
// We exec a nohup command that is backgrounded which returns immediately
foreach ($items as $item) {
exec("nohup php proc.php $id $item > $dir/proc.$id.$item.out &");
}
echo "<pre>";
// Run until timeout or all processing has finished
while(time() - $start < $timeout)
{
echo (time() - $start), " seconds\n";
clearstatcache(); // Required since PHP will cache for file_exists
$running = array();
foreach($items as $item)
{
// If the pid file still exists the process is still running
if (file_exists("$dir/proc.$id.$item.pid")) {
$running[] = $item;
}
}
if (empty($running)) break;
echo implode($running, ','), " running\n";
flush();
sleep(1);
}
// Clean up if we timeout out
if (!empty($running)) {
clearstatcache();
foreach ($items as $item) {
// Kill process of anything still running (i.e. that has a pid file)
if(file_exists("$dir/proc.$id.$item.pid")
&& $pid = file_get_contents("$dir/proc.$id.$item.pid")) {
posix_kill($pid, 9);
unlink("$dir/proc.$id.$item.pid");
// Would want to log this in the real world
echo "Failed to process: ", $item, " pid ", $pid, "\n";
}
// delete the useless data
unlink("$dir/proc.$id.$item.out");
}
} else {
echo "Successfully processed all items in ", time() - $start, " seconds.\n";
foreach ($items as $item) {
// Grab the processed data and delete the file
echo(file_get_contents("$dir/proc.$id.$item.out"));
unlink("$dir/proc.$id.$item.out");
}
}
echo "
";
?>
proc.php
<?php
$dir = realpath(dirname(__FILE__));
$id = $argv[1];
$item = $argv[2];
// Write out our pid file
file_put_contents("$dir/proc.$id.$item.pid", posix_getpid());
for($i=0;$i<80;++$i)
{
echo $item,':', $i, "\n";
usleep(250000);
}
// Remove our pid file to say we're done processing
unlink("proc.$id.$item.pid");
?>
Поместите test.php и proc.php в одну папку вашего сервера, загрузите test.php и наслаждайтесь.
Вам, конечно, понадобятся nohup (unix) и PHP cli, чтобы заставить это работать.
Очень весело, я могу найти применение позже.