Выполните сразу несколько команд exec (но дождитесь завершения последней)
Я искал это, и я не могу найти никого, кто пытается сделать именно то, что я.
У меня есть информация, которая передается в мою функцию через запрос _POST. На основе этих данных я запускаю команду exec для запуска сценария TCL определенное количество раз (с различными параметрами, основанными на переменной post). Прямо сейчас у меня есть exec в foreach, так что это займет вечность (сценарий TCL занимает 15 или около того секунд, чтобы вернуться, поэтому, если мне нужно запустить его 100 times, у меня есть небольшая проблема). Вот мой код:
public function executeAction(){
//code to parse the _POST variable into an array called devices
foreach($devices as $devID){
exec("../path/to/script.tcl -parameter1 ".$device['param1']." -parameter2 ".$device['param2'], $execout[$devID]);
}
print_r($execout);
}
очевидно, что этот код - просто отрывок с удаленными большими кусками, но, надеюсь, этого достаточно, чтобы продемонстрировать, что я пытаюсь сделать.
Мне нужно запустить всех исполнителей сразу, и мне нужно дождаться их завершения перед возвращением. Мне также нужен вывод всех скриптов, хранящихся в массиве под названием $execout.
какие идеи?
спасибо!!!
5 ответов
если вы положите exec()
вызов в отдельном скрипте, вы можете вызвать этот внешний скрипт несколько раз параллельно, используя curl_multi_exec()
. Таким образом, вы бы сделали все вызовы в отдельных запросах, чтобы они могли выполняться одновременно. Опрос &$still_running
чтобы увидеть, когда все запросы выполнены, после чего вы можете забрать результаты.
обновление: здесь блоге подробно именно то, что я описывающее.
пример
на основе сообщения в блоге, связанного выше, я собрал следующий пример.
скрипт запускается параллельно:
// waitAndDate.php
<?php
sleep((int)$_GET['time']);
printf('%d secs; %s', $_GET['time'], shell_exec('date'));
скрипт делает звонки параллельно:
// multiExec.php
<?php
$start = microtime(true);
$mh = curl_multi_init();
$handles = array();
// create several requests
for ($i = 0; $i < 5; $i++) {
$ch = curl_init();
$rand = rand(5,25); // just making up data to pass to script
curl_setopt($ch, CURLOPT_URL, "http://domain/waitAndDate.php?time=$rand");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_multi_add_handle($mh, $ch);
$handles[] = $ch;
}
// execute requests and poll periodically until all have completed
$isRunning = null;
do {
curl_multi_exec($mh, $isRunning);
usleep(250000);
} while ($isRunning > 0);
// fetch output of each request
$outputs = array();
for ($i = 0; $i < count($handles); $i++) {
$outputs[$i] = trim(curl_multi_getcontent($handles[$i]));
curl_multi_remove_handle($mh, $handles[$i]);
}
curl_multi_close($mh);
print_r($outputs);
printf("Elapsed time: %.2f seconds\n", microtime(true) - $start);
вот некоторые выходные данные, которые я получил при запуске его несколько раз:
Array
(
[0] => 8 secs; Mon Apr 2 19:01:33 UTC 2012
[1] => 8 secs; Mon Apr 2 19:01:33 UTC 2012
[2] => 18 secs; Mon Apr 2 19:01:43 UTC 2012
[3] => 11 secs; Mon Apr 2 19:01:36 UTC 2012
[4] => 8 secs; Mon Apr 2 19:01:33 UTC 2012
)
Elapsed time: 18.36 seconds
Array
(
[0] => 22 secs; Mon Apr 2 19:02:33 UTC 2012
[1] => 9 secs; Mon Apr 2 19:02:20 UTC 2012
[2] => 8 secs; Mon Apr 2 19:02:19 UTC 2012
[3] => 11 secs; Mon Apr 2 19:02:22 UTC 2012
[4] => 7 secs; Mon Apr 2 19:02:18 UTC 2012
)
Elapsed time: 22.37 seconds
Array
(
[0] => 5 secs; Mon Apr 2 19:02:40 UTC 2012
[1] => 18 secs; Mon Apr 2 19:02:53 UTC 2012
[2] => 7 secs; Mon Apr 2 19:02:42 UTC 2012
[3] => 9 secs; Mon Apr 2 19:02:44 UTC 2012
[4] => 9 secs; Mon Apr 2 19:02:44 UTC 2012
)
Elapsed time: 18.35 seconds
надеюсь, что это поможет!
одна сторона Примечание: убедитесь, что ваш веб-сервер может обрабатывать много параллельные запросы. Если он служит им последовательно или может служить только очень немногим одновременно, этот подход принесет вам мало или ничего. :-)
функция exec PHP всегда будет ждать ответа от вашего выполнения. Однако вы можете отправить stdout & stderror процесса в /dev / null (в unix) и выполнить все эти скрипты почти мгновенно. Это можно сделать путем добавления..
'> /dev/null 2>&1 &'
до конца строки исполнения.
но! это означает, что они раскошелятся и закончат обработку самостоятельно. Возможно, стоит попросить их написать ответ где-нибудь. И вы могли бы создать слушателя для собрать и обработать его.
цитирование документации PHP:
Примечание:
Если программа запущена с этой функцией, для того, чтобы продолжать работать в фоновом режиме, вывод программы должен быть перенаправлен в файл или другой поток. Если этого не сделать, PHP будет висеть до завершения выполнения программы.
таким образом, вы можете exec в фоновом режиме, если вы перенаправляете вывод в файл:
exec("../path/to/script.tcl -parameter1 ".$device['param1']." -parameter2 ".$device['param2']." > outputfile.txt", $execout[$devID]);
но если вы хотите подождите, пока все исполнители закончат, прежде чем продолжить, Вы должны сделать обратный вызов из внешнего скрипта. Может быть, так:
exec("../path/to/script.tcl -parameter1 ".$device['param1']." -parameter2 ".$device['param2']." > ../path/to/outputfile.txt; ".PHP_BINDIR.DIRECTORY_SEPARATOR."php ../path/to/callback.php", $execout[$devID]);
вот так, ваш обратный вызов.PHP скрипт будет вызываться после каждого выполнения скрипта.язык Tcl.
может быть, вы можете сделать что-то с этими трюками.
вам нужно немного изменить свой скрипт
- сохранить данные post в сеанс
- Exec, сохранить результат в сеанс
- используйте перенаправление с помощью JavaScript
- команда echo redirect после возврата exec с тем же URL-адресом, но добавить инкрементный индекс, например ?индекс=99
- когда индекс достигает конца, показать весь результат
посмотрите на ExecFuture и futureiterator в библиотеке libputil:
https://secure.phabricator.com/book/libphutil/class/ExecFuture/
Это именно то, что вам нужно, с довольно приятным синтаксис:
$futures = array();
foreach ($files as $file) {
$futures[$file] = new ExecFuture("gzip %s", $file);
}
foreach (new FutureIterator($futures) as $file => $future) {
list($err, $stdout, $stderr) = $future->resolve();
if (!$err) {
echo "Compressed {$file}...\n";
} else {
echo "Failed to compress {$file}!\n";
}
}