听起来很容易,但解决起来真的很难。原因是竞争条件。
什么是竞争条件?
如果您打开计数器文件、读取内容、增加点击量并将点击量写入文件内容,那么在所有这些步骤之间可能会发生许多事情,因为其他访问者同时在您的网站上打开相同的脚本。想一想当第一个访问者请求(线程)将“484049”按字符写入计数器文件并在毫秒内写入“484”时的情况,第二个线程读取该值并将其增加到“485”失去大部分你的热门歌曲。
不要使用全局锁!
也许您考虑使用LOCK_EX
. 这样,第二个线程需要等到第一个线程完成对文件的写入。但是“等待”并不是你真正想要的。这意味着每个线程,我的意思是每个线程都需要等待其他线程。您的网站上只需要一些狂暴的机器人、许多访问者或驱动器上的临时 i/o 问题,并且在所有写入完成之前没有人能够加载您的网站......如果访问者无法打开您的网站会发生什么...他会刷新它,导致新的等待/锁定线程...瓶颈!
使用基于线程的锁
唯一安全的解决方案是立即为同时运行的线程创建一个新的计数器文件:
<?php
// settings
$count_path = 'count/';
$count_file = $count_path . 'count';
$count_lock = $count_path . 'count_lock';
// aquire non-blocking exlusive lock for this thread
// thread 1 creates count/count_lock0/
// thread 2 creates count/count_lock1/
$i = 0;
while (file_exists($count_lock . $i) || !@mkdir($count_lock . $i)) {
$i++;
if ($i > 100) {
exit($count_lock . $i . ' writable?');
}
}
// set count per thread
// thread 1 updates count/count.0
// thread 2 updates count/count.1
$count = intval(@file_get_contents($count_file . $i));
$count++;
//sleep(3);
file_put_contents($count_file . $i, $count);
// remove lock
rmdir($count_lock . $i);
?>
现在,您的计数器文件夹中有 , 等,而count/count.1
将捕获大部分命中。原因是竞争条件并非一直发生。它们仅在两个线程同时发生时才会发生。count/count.2
count.1
注意:如果您看到(远)超过 2 个文件,这意味着与您拥有的访问者数量相比,您的服务器确实很慢。
如果你现在想要总点击数,你需要整理它们(在这个例子中是随机的):
<?php
// tidy up all counts (only one thread is able to do that)
if (mt_rand(0, 100) == 0) {
if (!file_exists($count_lock) && @mkdir($count_lock)) {
$count = intval(@file_get_contents($count_file . 'txt'));
$count_files = glob($count_path . '*.*');
foreach ($count_files as $file) {
$i = pathinfo($file, PATHINFO_EXTENSION);
if ($i == 'txt') {
continue;
}
// do not read thread counts as long they are locked
if (!file_exists($count_lock . $i) && @mkdir($count_lock . $i)) {
$count += intval(@file_get_contents($count_file . $i));
file_put_contents($count_file . $i, 0);
rmdir($count_lock . $i);
}
}
file_put_contents($count_file . 'txt', $count);
rmdir($count_lock);
}
}
// print counter
echo intval(@file_get_contents($count_file . 'txt'));
?>
PS启用sleep(3)
并查看计数器文件夹以模拟慢速服务器,您会看到多个计数文件的增长速度。