我需要打开一个日志文件进行写作。麻烦的是,许多事情可能同时做到这一点,我不想要冲突。每次写入都是一行,通常大约150个字节(并且总是小于1K),并且不是严格按照时间顺序获取内容。
我想我想要的是尝试flock()
,如果失败,请继续尝试几秒钟。如果在多次尝试后无法建立锁定,则放弃。
$fh=fopen($logfile, "a");
if (flock($fh, LOCK_EX|LOCK_NB)) {
$locked=TRUE;
} else {
$locked=FALSE;
// Retry lock every 0.1 seconds for 3 seconds...
$x=0; while($x++ < 30) {
usleep(100000);
if (flock($fh, LOCK_EX|LOCK_NB)) {
$locked=TRUE;
break;
}
}
}
if ($locked) {
if (fwrite($fh, strftime("[%Y-%m-%d %T] ") . $logdata . "\n")) {
print "Success.\n";
} else {
print "Fail.\n";
}
flock($fh, LOCK_UN)
} else {
print "Lock failed.\n";
}
我有两个问题,一个是普通的,一个是具体的。首先,除了以不同的方式(do...while
等)实现相同的解决方案之外,是否有更好的通用策略来处理这种仅在PHP中运行的问题?其次,有没有更好的方法在PHP中实现它? (是的,我将这些分开了,因为我对战略部分非常感兴趣。)
我考虑过的一个替代方案是使用syslog(),但PHP代码可能需要在系统级管理(即向/etc/syslog.conf添加内容)可能无法使用的平台上运行一个选项。
根据{{3}}的建议, 更新:将|LOCK_NB
添加到上面的代码中。
答案 0 :(得分:4)
我在PHP制作日志方面的长期经验(在linux下!)我从未遇到过冲突问题(即使有数百个同时写入和并发写入)。如此简单的跳过锁定管理:
$fh=fopen($logfile, "a");
if (fwrite($fh, strftime("[%Y-%m-%d %T] ") . $logdata . "\n")) {
print "Success.\n";
} else {
print "Fail.\n";
}
fclose($fh);
关于此策略,文件记录(带锁或不带锁)不是最佳解决方案,因为每个带有“ a ”的fopen都意味着设置了一个搜索系统调用光标在文件的末尾。 syslog 保持文件打开可以避免这种开销。
当然,开销在“大”文件中变得有意义(在性能上),一个简单的解决方案是在名称中创建日期(或日期时间)的日志文件。
ADD
apache包中包含一个测试程序: ab ,允许在并发中进行查询,你可以测试我的论文,用10到1000个线程完成的1000000查询来强调你的服务器。
ADD - 发表评论
不,这不是一项不可能完成的任务。
我从http://php.net/manual/en/function.fwrite.php
找到了注释如果handle在附加模式下被fopen()编辑,则fwrite()s是原子的(除非 在某些情况下,字符串的大小超过了文件系统的块大小 平台,只要文件在本地文件系统上)。那是, 在调用fwrite()之前不需要flock()资源;所有的 数据将不间断地写入。
知道块的大小(通常为4k):
dumpe2fs / dev / sd_your_disk_partition | less -i
写入的“原子性”实现阻止其他“代理”写入(当您在“ps ax”中看到“D”状态的进程时),但是 PHP 流工具< / strong>可以解决这个问题,请参阅:* stream_set_blocking *。 这种方法可能会引入部分写入,因此您必须验证记录的完整性。
在任何情况下, fwrite (网络或文件)都容易受到阻止/失败,无论使用 flock 。 IMHO flock只引入了开销。
关于您的原始问题和您的目标(尝试在高度风险厌恶的环境中实施公司政策),即使是一个有问题的fwrite,我只能想象一个简单的解决方案:使用数据库