English 中文(简体)
在PHP中锁定NFS文件
原标题:
  • 时间:2008-10-20 13:36:04
  •  标签:

我最新的Web应用程序的一部分需要写入文件以用作日志记录的一部分。我发现的一个问题是,如果有几个并发用户,则写入可能会互相覆盖(而不是附加到文件中)。我认为这是因为目标文件可以同时在多个位置打开。

“flock(...)”通常很好,但似乎在NFS上不起作用...这对我来说是一个巨大的问题,因为生产服务器使用NFS阵列。”

我所见过的最接近实际解决方案的东西是尝试创建一个锁定目录并等待直到它可以被创建。说这缺乏优雅是对今年甚至十年来的低估。

有更好的主意吗?

编辑:我应该补充一下,我没有服务器的root权限,而且以另一种方式存储并不容易实现,尤其是在我的截止日期内。

最佳回答

另一个肮脏的黑客方式是使用flock()锁定“本地”文件,只有在锁定本地文件时才打开/写入NFS文件。

编辑:来自flock()页面:

flock() will not work on NFS and many other networked file systems. Check your operating system documentation for more details.

编辑2:

当然,如果您的应用程序使用数据库,始终可以使用数据库来同步访问。(我假设您的应用程序使用数据库)。但是,如果您需要大量记录操作,这可能会对性能造成一定影响。

如果只是为了记录日志,您真的需要一个集中的日志文件吗?您可以本地记录日志(如果需要,甚至可以在一天结束时合并日志)。

问题回答

Directory operations are NOT atomic under NFSv2 and NFSv3 (please refer to the book NFS Illustrated by Brent Callaghan, ISBN 0-201-32570-5; Brent is a NFS-veteran at Sun).

NFSv2有两个原子操作:

  • symlink
  • rename

在NFSv3中,创建调用也是原子的。

Knowing this, you can implement spin-locks for files and directories (in shell, not PHP):

锁定当前目录:

while ! ln -s . lock; do :; done

锁定文件。

while ! ln -s ${f} ${f}.lock; do :; done 

解锁(假设,正在运行的进程确实获得了锁)

解锁当前目录:

mv lock deleteme && rm deleteme

un锁定文件。

mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme

Remove is also not atomic, therefore first the rename (which is atomic) and then the remove.

For the symlink and rename calls, both filenames have to reside on the same filesystem. My proposal: use only simple filenames and put file and lock into the same directory.

一个方法是设置一个共享的Memcache实例在您的每个虚拟服务器之间。当您开始本地文件操作时,您可以通过将文件名的条目放入缓存来模仿flock(),并在完成后清除它。

每个服务器在进行文件操作之前都可以访问此池,并查看是否存在此“锁”。

// Check for lock, using $filename as key
$lock = $memcache->get($filename);

if(!$lock) {
    // Set lock in memcache for $filename
    $memcache->set($filename, 1);

    // Do file operations...

    // Blow away "lock"
    $memcache->delete($filename);
}

不是最优雅的解决方案, 但可以相对轻松地让你从你的设置中的所有服务器控制锁定。

您也可以使用 dio_fcntl() 在NFS卷上锁定文件。这需要dio包,可能不会默认包含在您的php安装中。

尽管您不能在NFS上flock()文件,且I / O可以是异步的,但是NFS上的目录操作确实是原子操作。这意味着在任何给定时间,目录存在或不存在。

要实现自己的NFS锁定功能,请在您想锁定它时检查或创建目录,并在完成后将其删除。

很遗憾,它可能与你自己没有编写的任何其他应用程序不兼容。

应该只使用memcache add并避免竞态条件。

if ($memcache->add($filename, 1, 1))
{
   $memcache->delete($filename);
}




相关问题
热门标签