问题描述
我需要打开一个日志文件进行写入.麻烦的是,很多事情可能同时发生,我不想发生冲突.每次写入都将是一行,通常大约为150个字节(且始终小于1K),并且严格要求按时间顺序进行存储.
I need to open a log file for writing. Trouble is, many things may do this at the same time, and I don't want conflicts. Each write will be a single line, generally about 150 bytes (and always less than 1K), and getting things in chronological order is not strictly required.
我思考我想要的是尝试flock()
,如果失败,请继续尝试几秒钟.如果尝试多次后仍无法建立锁,请放弃.
I think what I want is to attempt to flock()
, and if it fails, keep trying for a few seconds. If a lock can't be established after a number of tries, then give up.
$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中实现呢? (是的,我将它们分开是因为我对策略部分真的很感兴趣.)
I have two questions, one general and one specific. First, aside from implementing the same solution in different ways (do...while
, etc), is there a better general strategy for handling this kind of problem, that runs solely in PHP? Second, is there a better way of implementing this in PHP? (Yes, I separated these because I'm really interested in the strategy part.)
我考虑过的另一种方法是使用 syslog(),但是PHP代码可能需要运行在可能无法选择系统级管理(即向/etc/syslog.conf中添加内容)的平台上.
One alternative I've considered is to use syslog(), but the PHP code may need to run on platforms where system-level administration (i.e. adding things to /etc/syslog.conf) may not be available as an option.
更新:根据 randy 的建议,在上面的代码中添加了|LOCK_NB
.
UPDATE: added |LOCK_NB
to the code above, per randy's suggestion.
推荐答案
以我在PHP中制作日志的长期经验(在Linux下!),我从未遇到过冲突问题(即使有数百个同时进行和并发写入的情况).如此简单的跳过锁管理:
In my long experience in PHP making logs (under linux!) I've never experienced problems of conflicts (even with hundreds of simultaneus and concurrent writes). So simple skip lock maagement:
$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 保持文件打开以避免这种开销.
About this strategy the file logging (with or without lock) is not the best solution, because every fopen with the "a" imply a seek syscall to set the cursor at the end of the file. syslog keeping the file open avoid this overhead.
当然,对于大"文件,开销会变得很重要(在性能上),一个简单的解决方案是创建名称为日期(或日期时间)的日志文件.
Of course the overhead become significative (in performance) with "big" files, an easy solution is create log file with date (or date-time) in the name.
添加
apache程序包包含一个测试程序: ab ,允许并发执行查询,您可以测试我的论文,用10to1000个线程完成1000000个查询来强调服务器.
apache package include a tester program: ab, allowing doing query in concurrency, you can test my thesis stressing your server with 1000000 of query done by 10to1000 threads.
添加-在评论之后
不,这不是不可能的任务.
No, it'not an impossible task.
我从 http://php中找到了便笺. net/manual/en/function.fwrite.php
I found a note from http://php.net/manual/en/function.fwrite.php
了解一个字节的块有多大(通常为4k):
to know how big is a block in bytes (usual 4k):
实现写入的原子性"是为了阻止其他代理"进行写入(当您在"ps ax"中看到处于"D"状态的进程)时,但是 PHP 流设施可以解决此问题,请参阅:* stream_set_blocking *.这种方法可能会引入部分写入,因此您将必须验证记录的完整性.
The "atomicity" of write is realized blocking other "agents" to write (when you see process in "D" status in "ps ax"), BUT PHP stream facility can solve this problem, see: *stream_set_blocking*.This approach can introduce partial writes, so you will have to validate the integrity of your records.
无论如何, fwrite (网络或文件)很容易被阻止/失败,无论使用 flock 如何.恕我直言,羊群只会带来开销.
In any case a fwrite (network or file) is susceptible to block/failure regardless of use of flock. IMHO flock introduce only overhead.
关于您最初的问题和您的目标(试图在高度规避风险的环境中实施公司政策),即使fwrite也可能有问题,我只能想象一个简单的解决方案:使用数据库
About your original questions AND your goal (trying to implement corporate policy in a highly risk-averse environment), where even a fwrite can be problematic, I can only imagine a simple solution: use a DB
- 最复杂的部分不是PHP,而是用C编写的!
- 完全控制操作流程
- 并发级别
这篇关于如何在一段时间内重试PHP flock()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!