У меня есть скрипт PHP, который использует exec
функция для выполнения сценария Node и возврата некоторых данных обратно в тот же сценарий PHP.
Моя проблема в том, что мне нужно вернуть данные обратно в PHP, не дожидаясь очистки кода в finally
заканчивать.
Я написал несколько примеров кода ниже, чтобы показать вам, как работает код, и проиллюстрировать мою проблему. В примере кода не используются никакие модули узлов, но вы можете использовать их, если они помогут.
example.php
$data = 'hello world';
$exec = 'node example.js ' . $data;
$escaped_command = escapeshellcmd($exec);
$data = exec($escaped_command);
// waits 10 seconds then displays returned data
// I need the code to return before finally executes
var_dump($data); // string(31) "Final data is final hello world"
example.js
let data = process.argv[2];
// let data = "hello world";
async function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const doSomething = data => new Promise((resolve, reject) => {
if (data) {
resolve("new hello world");
} else {
reject();
}
});
const doSomethingElse = newData => new Promise((resolve, reject) => {
if (newData) {
resolve("final hello world");
} else {
reject();
}
});
(async() => {
try {
let newData = await doSomething(data);
let finalData = await doSomethingElse(newData);
// return this to server/php right now.
// Do not wait until finally script finishes
console.log("Final data is " + finalData);
} catch (err) {
// if error return this to server/php right now.
// Do not wait until finally script finishes
console.log(err);
} finally {
// does cleanup/closes node app
await timeout(10000);
}
})();
использование proc_open запустить процесс асинхронно. Позвольте узлу выводить данные как можно скорее. Эти данные могут быть прочитаны без ожидания завершения узла:
// foo.js
process.stdout.write(JSON.stringify({"foo":"bar"})+"\n");
// simulate cleanup
setTimeout(() => {}, 3000);
// foo.php
<?php
$io = [
0 => ['pipe', 'r'], // node's stdin
1 => ['pipe', 'w'], // node's stdout
2 => ['pipe', 'w'], // node's stderr
];
$proc = proc_open('node foo.js', $io, $pipes);
$nodeStdout = $pipes[1]; // our end of node's stdout
echo date('H:i:s '), fgets($nodeStdout);
proc_close($proc);
echo date('H:i:s '), "done\n";
Образец вывода:
$ php foo.php
14:59:03 {"foo":"bar"}
14:59:06 done
proc_close будет ожидать завершения процесса узла, но вы можете отложить это ожидание, пока не закончите с другими вещами в PHP.
Если вы не вызываете proc_close, вы можете столкнуться с процессами зомби, в зависимости от вашей среды. Docker и проблема пожатия зомби PID 1 содержит хорошее объяснение. Это во многом относится к докеру, потому что именно здесь часто встречаются зомби, но это все же в целом применимо.
В некоторых серверных API есть условия для запуска кода «незаметно» для клиента. Например, fcgi имеет fastcgi_finish_request, который вы можете вызвать до proc_close. Тогда клиенты HTTP не заметят время ожидания.
Если вы не можете терпеть в конечном итоге ожидание узла, подумайте о том, чтобы сделать его вместо этого веб-сервисом, который управляется независимо от PHP.
Других решений пока нет …