目标/动机
我想写一个服务,它应该一直运行。但是,在服务已经运行时,应该不可能再次启动该服务。
用例
用户X打开页面myService.php,然后单击页面上的按钮启动服务。之后关闭浏览器。过了一段时间,另一个用户打开浏览器并尝试启动服务。但是该服务应该只启动一次。
(SEMI(伪码
<?php
ignore_user_abort(TRUE);
set_time_limit(0);
$sleepTime = 60;
while(1){
if( serviceAlreadyStarted() ){
echo "Service has already been started.";
exit;
}
if( shouldServiceBeStopped() ){
echo "Service has been stopped.";
exit;
}
// DO SOMETHING
sleep($sleepTime);
}
?>
预期行为
在第二次调用再次打开服务时,用户会立即收到消息。
实际行为
脚本的第二个调用没有响应,直到第二个条件shouldServiceBeStopped((退出的第一个调用
tldr:不要让用户进入第二个循环,在他们可以按下按钮进入循环之前,请检查服务是否正在运行。
我会在数据库或JSON文件中存储一个值(ini文件也可以(。然后,当用户第一次加载页面时,读取该值并查看它是否已经启动。然后,您可以直接向用户显示流程已经启动的消息,而不是让他们按下按钮启动流程,然后向他们显示消息。它省去了一个步骤,让用户更容易,还应该解决第二个循环一直运行到第一个循环停止的问题。
问题是,如果脚本在运行时崩溃,则会将文件标记为打开。有两种方法可以解决这个问题。第一个是stopService
函数可以强制文件退出,在where循环中,如果检测到该值,则退出。不过,这需要用户停止服务。第二种方法,也是我建议的方法,是记录服务的日期/时间戳,每次循环开始时,都会更新文件中的日期/时刻。然后在serviceAlreadyStarted()
函数中,您可以检查日期/时间是否在有效期内。如果不是,你知道脚本崩溃了,可以向用户显示错误消息。
更新
我注意到你添加了一条评论,说你几乎已经这样做了,但文件仍然打开。请确保在读取/设置值后立即关闭文件。也有可能是循环执行得太快,以至于打开和关闭文件的速度太快,第二个副本也无法访问它。如果是这种情况,我只允许循环每100个循环打开一次文件,这应该允许程序的其他实例也访问该文件。
@Styphon
我已经尝试了很多可能的方法,但都没有成功。请考虑下面的代码。我将描述一些对我来说毫无意义的具体案例
案例1文件serviceStarted.txt存在。没有正在进行的呼叫。我调用脚本,得到的答案是"服务已经启动"。没有延误。
案例2serviceStarted.txt不存在。我称之为剧本。第一个循环正在运行,文件已创建。使用另一个选项卡或浏览器,我再次调用该脚本。大约需要20秒,直到我收到服务正在运行的消息。
案例3(最有趣的一个(与案例#2类似,但我在20秒之前(即第一次调用后4秒(在相关文件夹中手动创建/复制serviceShouldBeStopped.txt。这导致第一和第二循环同时停止。这不是有点奇怪吗?
// Very first check:
if (is_file("serviceStarted.txt")) {
echo "<br> Service has already been started.";
} else {
while (1) {
// This check is aquivalent to the shouldServiceBeStopped()
// I copy this txt manually to the folder in order to stopp the first call
if (is_file("serviceShouldStopped.txt")) {
echo "Service stopped.";
exit;
}
// The first call should create this file
if (!is_file("serviceStarted.txt"))
file_put_contents("serviceStarted.txt", "any content");
sleep(60);
}
}
更新
// This if statement used in second call
if (isset($_GET['noloop'])) {
echo "loop should not be executed";
}
else {
while (1) {
// This check is aquivalent to the shouldServiceBeStopped()
// I copy this txt manually to the folder in order to stopp the first call
if (file_exists("serviceShouldStopped.txt")) {
echo "Service stopped at " . date("H:i:s");
exit;
}
sleep(60);
}
}