我的本意是这样的。
我client.html呼吁通过AJAX PHP脚本check.php。 我想check.php检查,如果另一个脚本task.php已经正在运行。 如果是,我什么都不做。 如果不是,我需要在后台运行它。
我有一个想法,就是我想做的事,但我不确定如何做到这一点。
A部分:我知道如何通过AJAX调用check.php。
B部分在check.php我可能需要运行task.php。 我想,我需要这样的东西:
$PID = shell_exec("php task.php > /dev/null & echo $!");
我觉得“>的/ dev / null的与”位告诉它在后台运行,但我不确定什么是“$!” 确实。
部分C.该$ PID,我需要为过程的标签。 我需要写这个号码(或其他)在同一目录中的文件,并需要读它每次调用check.php。 我不知道如何做到这一点。 可能有人给我如何读/写单号文件到同一目录的链接?
部分D.然后检查上次启动task.php仍然在运行,我要使用的功能:
function is_process_running($PID)
{
exec("ps $PID", $ProcessState);
return(count($ProcessState) >= 2);
}
我认为这是我需要的所有位,但你可以看到我是如何做他们几个不确定。
Answer 1:
我会用一个flock()
基础的机制,以确保task.php
只运行一次。
使用这样的代码:
<?php
$fd = fopen('lock.file', 'w+');
// try to get an exclusive lock. LOCK_NB let the operation not blocking
// if a process instance is already running. In this case, the else
// block will being entered.
if(flock($fd, LOCK_EX | LOCK_NB )) {
// run your code
sleep(10);
// ...
flock($fd, LOCK_UN);
} else {
echo 'already running';
}
fclose($fd);
还要注意的是flock()
时,作为PHP文件指出,在所有便携式支持的操作系统。
!$
让你在bash最后执行的程序的PID。 像这样:
command &
pid=$!
echo pid
请注意,您必须确保你的PHP代码使用bash支持的系统上运行。 (不是Windows)
更新 (后首战的评论)。
flock()
将在所有操作系统(正如我所提到)。 我在你的代码看到窗口工作时的问题是!$
正如我前面提到;)..
为了得到你应该使用task.php的PID proc_open()
开始task.php。 我已经准备了两个示例脚本:
task.php
$fd = fopen('lock.file', 'w+');
// try to get an exclusive lock. LOCK_NB let the operation not blocking
// if a process instance is already running. In this case, the else
// block will being entered.
if(flock($fd, LOCK_EX | LOCK_NB )) {
// your task's code comes here
sleep(10);
// ...
flock($fd, LOCK_UN);
echo 'success';
$exitcode = 0;
} else {
echo 'already running';
// return 2 to let check.php know about that
// task.php is already running
$exitcode = 2;
}
fclose($fd);
exit($exitcode);
check.php
$cmd = 'php task.php';
$descriptorspec = array(
0 => array('pipe', 'r'), // STDIN
1 => array('pipe', 'w'), // STDOUT
2 => array('pipe', 'w') // STDERR
);
$pipes = array(); // will be set by proc_open()
// start task.php
$process = proc_open($cmd, $descriptorspec, $pipes);
if(!is_resource($process)) {
die('failed to start task.php');
}
// get output (stdout and stderr)
$output = stream_get_contents($pipes[1]);
$errors = stream_get_contents($pipes[2]);
do {
// get the pid of the child process and it's exit code
$status = proc_get_status($process);
} while($status['running'] !== FALSE);
// close the process
proc_close($process);
// get pid and exitcode
$pid = $status['pid'];
$exitcode = $status['exitcode'];
// handle exit code
switch($exitcode) {
case 0:
echo 'Task.php has been executed with PID: ' . $pid
. '. The output was: ' . $output;
break;
case 1:
echo 'Task.php has been executed with errors: ' . $output;
break;
case 2:
echo 'Cannot execute task.php. Another instance is running';
break;
default:
echo 'Unknown error: ' . $stdout;
}
你问我为什么我的羊()解决方案是最好的。 这只是因为对方的回答就不能可靠地确保task.php
运行一次。 这是因为我在这个问题的答案下面的评论中提到的竞争条件。
Answer 2:
你可以实现它,使用锁文件:
if(is_file(__DIR__.'/work.lock'))
{
die('Script already run.');
}
else
{
file_put_contents(__DIR__.'/work.lock', '');
// YOUR CODE
unlink(__DIR__.'/work.lock');
}
Answer 3:
太糟糕了,我没有看到这一点,被接受之前..
我写了一个类来实现这个功能。 (使用文件锁)和PID,进程ID检查,在Windows和Linux操作系统。
https://github.com/ArtisticPhoenix/MISC/blob/master/ProcLock.php
Answer 4:
我觉得你真的是所有的流程和背景调查矫枉过正。 如果你运行一个PHP
脚本without a session
,那么您已基本在后台运行它。 因为它不会阻挡来自用户的任何其它请求。 所以一定要确保你不调用session_start();
然后,下一个步骤将是,即使当用户取消该请求,这是在一个基本功能来运行它PHP
。 ignore_user_abort
最后检查,以确保它是只运行一次,可与创建文件,可以轻松完成,因为PHP
可是没有一个简单的应用范围。
联合:
<?php
// Ignore user aborts and allow the script
// to run forever
ignore_user_abort(true);
set_time_limit(0);
$checkfile = "./runningtask.tmp";
//LOCK_EX basicaly uses flock() to prevents racecondition in regards to a regular file open.
if(file_put_contents($checkfile, "running", LOCK_EX)===false) {
exit();
}
function Cleanup() {
global $checkfile;
unlink($checkfile);
}
/*
actual code for task.php
*/
//run cleanup when your done, make sure you also call it if you exit the code anywhere else
Cleanup();
?>
在JavaScript您现在可以直接调用task.php并在已建立到服务器的连接取消请求。
<script>
function Request(url){
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
} else{
return false;
}
httpRequest.onreadystatechange = function(){
if (httpRequest.readyState == 1) {
//task started, exit
httpRequest.abort();
}
};
httpRequest.open('GET', url, true);
httpRequest.send(null);
}
//call Request("task.php"); whenever you want.
</script>
加分点:可以有task.php写不定期的更新,以实际的代码$checkfile
有正在发生的事情的感觉。 然后你就可以有另一种AJAX文件中读出的内容和显示状态给用户。
Answer 5:
让我们从B到d简单使整个过程
步骤BD:
$rslt =array(); // output from first exec
$output = array(); // output of task.php execution
//Check if any process by the name 'task.php' is running
exec("ps -auxf | grep 'task.php' | grep -v 'grep'",$rslt);
if(count($rslt)==0) // if none,
exec('php task.php',$output); // run the task,
说明:
ps -auxf --> gets all running processes with details
grep 'task.php' --> filter the process by 'task.php' keyword
grep -v 'grep' --> filters the grep process out
注意:
它也建议把同样的检查中task.php文件。
如果task.php经由的httpd(web服务器)直接执行,它只会被显示为httpd进程,并且不能由“PS”命令来识别
它不会负载平衡的环境下工作。 [编辑:17Jul17]
Answer 6:
你可以在脚本本身的独占锁的脚本运行的持续时间
任何其他尝试运行它会尽快锁定()函数被调用结束。
//try to set a global exclusive lock on the file invoking this function and die if not successful
function lock(){
$file = isset($_SERVER['SCRIPT_FILENAME'])?
realpath($_SERVER['SCRIPT_FILENAME']):
(isset($_SERVER['PHP_SELF'])?realpath($_SERVER['PHP_SELF']):false);
if($file && file_exists($file)){
//global handle stays alive for the duration if this script running
global $$file;
if(!isset($$file)){$$file = fopen($file,'r');}
if(!flock($$file, LOCK_EX|LOCK_NB)){
echo 'This script is already running.'."\n";
die;
}
}
}
测试
在一个shell中运行这一点,并试图在另一运行它,而它正在等待输入。
lock();
//this will pause execution until an you press enter
echo '...continue? [enter]';
$handle = fopen("php://stdin","r");
$line = fgets($handle);
fclose($handle);
文章来源: Running a php script via ajax, but only if it is not already running