如何检测PHP脚本是否已在运行?

use*_*413 12 php shell

我有一个cron脚本,每10分钟执行一次PHP脚本.该脚本检查队列并处理队列中的数据.有时队列有足够的数据可以持续超过10分钟的处理,从而产生了两个脚本试图访问相同数据的可能性.我希望能够检测脚本是否已经运行以防止启动脚本的多个副本.我想过创建一个数据库标志,说明脚本正在处理,但如果脚本崩溃,它会使它处于正状态.有没有一种简单的方法可以判断PHP脚本是否已经使用PHP或shell脚本运行?

Mar*_*ery 35

您只需使用锁定文件即可.PHP的flock()函数为Unix 的函数提供了一个简单的包装器flock,它提供了对文件的建议锁.

如果您没有明确地释放它们,操作系统将在持有它们的进程终止时自动释放这些锁,即使它异常终止.

您还可以遵循松散的Unix惯例,即将锁定文件设置为"PID文件" - 也就是说,在获取文件锁定后,让脚本将其PID写入其中.即使你从未在脚本中读过这个内容,如果你的脚本挂起或发疯并且你想找到它的PID以便手动杀死它,它将会很方便.

这是一个复制/粘贴就绪的实现:

#!/usr/bin/php
<?php

$lock_file = fopen('path/to/yourlock.pid', 'c');
$got_lock = flock($lock_file, LOCK_EX | LOCK_NB, $wouldblock);
if ($lock_file === false || (!$got_lock && !$wouldblock)) {
    throw new Exception(
        "Unexpected error opening or locking lock file. Perhaps you " .
        "don't  have permission to write to the lock file or its " .
        "containing directory?"
    );
}
else if (!$got_lock && $wouldblock) {
    exit("Another instance is already running; terminating.\n");
}

// Lock acquired; let's write our PID to the lock file for the convenience
// of humans who may wish to terminate the script.
ftruncate($lock_file, 0);
fwrite($lock_file, getmypid() . "\n");

/*
    The main body of your script goes here.
*/
echo "Hello, world!";

// All done; we blank the PID file and explicitly release the lock 
// (although this should be unnecessary) before terminating.
ftruncate($lock_file, 0);
flock($lock_file, LOCK_UN);
Run Code Online (Sandbox Code Playgroud)

只需将锁定文件的路径设置为您喜欢的位置即可.

  • 这应该是最好的答案.这很有道理.[Symfony 2.6 LockHandler](http://symfony.com/blog/new-in-symfony-2-6-lockhandler)也通过文件锁实现锁. (2认同)

Mar*_*odF 6

如果你需要它绝对防止崩溃,你应该使用信号量,当php结束特定的请求处理时,信号量会自动释放.

一种更简单的方法是在执行开始时创建DB记录或文件,并在结尾处将其删除.您可以随时检查该记录/文件的"年龄",如果它比正常脚本执行的时间早3倍,则假设它已崩溃并将其删除.

没有"银弹",它只取决于你的需求.


小智 6

如果您正在运行Linux,这应该在您的脚本顶部工作:

$running = exec("ps aux|grep ". basename(__FILE__) ."|grep -v grep|wc -l");
if($running > 1) {
   exit;
}
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个解决方案的简单性。有什么缺点吗?另外,如果其他人想使用它 - 如果您在 crontab 中使用它,请检查该数字是否大于 2 而不是 1。 (2认同)