用PHP访问不带数据库的计数器
我有一个单一的网页,我想跟踪多少次,而不使用数据库访问 我想到了XML,每次用户访问页面时都会更新一个文件:用PHP访问不带数据库的计数器,php,counter,Php,Counter,我有一个单一的网页,我想跟踪多少次,而不使用数据库访问 我想到了XML,每次用户访问页面时都会更新一个文件: <?xml version='1.0' encoding='utf-8'?> <counter>8</counter> 8. 然后我认为在一个单独的文件中声明一个PHP计数器,然后在用户每次访问页面时更新它可能是一个更好的主意 counter.php <?php $counter = 0; ?> 更新_counter.p
<?xml version='1.0' encoding='utf-8'?>
<counter>8</counter>
8.
然后我认为在一个单独的文件中声明一个PHP计数器,然后在用户每次访问页面时更新它可能是一个更好的主意
counter.php
<?php
$counter = 0;
?>
更新_counter.php:
<?php
include "counter.php";
$counter += 1;
$var = "<?php\n\t\$counter = $counter;\n?>";
file_put_contents('counter.php', $var);
?>
这样,每次访问update\u counter.php
时,counter.php
文件中的变量都会递增
无论如何,我注意到,如果counter.php
文件具有$counter=5
,并且update\u counter.php
文件在同一时间被1000个用户访问,那么该文件在同一时间被读取1000次(因此在所有请求中都会读取5
值)counter.php
文件将更新为值5+1(=6)
而不是1005
有没有一种方法可以使它在不使用数据库的情况下工作?您可以使用flock()
来锁定文件,这样其他进程就不会写入该文件
编辑:更新为使用fread()
而不是include()
*/
//打开文件进行读取
$fp=fopen(“counterlog.txt”,“r”);
//获取现有计数
$count=fread($fp,1024);
//关闭文件
fclose($fp);
//将1添加到现有计数
$count=$count+1;
//显示点击数
//如果不想显示它,请注释掉这一行
echo“页面视图:”$计数“”;
//重新打开文件并删除内容
$fp=fopen(“counterlog.txt”,“w”);
fwrite($fp,$count);
//关闭文件
fclose($fp);
?>
这听起来很容易,但实际上很难解决。原因是
什么是比赛条件?如果你打开一个计数器文件,读取内容,增加点击次数并将点击次数写入文件内容,那么在所有这些步骤之间,通过其他访问者同时在你的网站上打开相同的脚本,可能会发生很多事情。想想当第一个访问者请求(线程)将“484049”命中数逐字符地写入计数器文件时的情况,在毫秒内写入“484”时,第二个线程读取该值并将其递增到“485”,从而丢失了大部分不错的命中数 不要使用全局锁
也许您可以考虑使用
LOCK\u 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);
?>
另外,启用
sleep(3)
并查看计数器文件夹以模拟慢速服务器,您会看到多个计数文件的增长速度有多快。以下功能运行良好,但文件大小过大,访问次数过多
file_put_contents('counter.txt', '1', FILE_APPEND);
echo '<h1>Hi, Page served ' . filesize('counter.txt') . ' times!</h1>';
file\u put\u内容('counter.txt',1',file\u APPEND);
回音“你好,传呼到了”。文件大小('counter.txt')。'时代;
但是,在文件达到1000或1000000后,只需创建另一个同样计算该单位的文件。大尺寸的不美观与不需要锁定的性能相匹配。您将想了解
flock()
@christophermorissey我真的不知道这个函数,您能解释一下它是什么以及如何使用它吗?我想使用flock()
您必须读取该文件,而不是将其包含在内。它会添加一点代码,但不会太难。问题可能在于,您必须在锁定文件之前打开该文件,并且可能有多个用户可以在获得锁之前打开该文件(这意味着多个用户将读取相同的初始值)@MichaelWheeler如果文件被锁定,是否意味着如果打开该文件,则另一个脚本打开该文件的请求将被拒绝?或者另一个脚本只是等待锁被释放?将此作为一个答案添加,因为我没有足够的声誉来评论-实际上,在cmorrissey的答案中的ftruncate()之后,需要插入以下行以确保在开始时写入文件。否则,多余的空值将阻止所需的结果。倒带($fp)@迈克尔·赫勒:哎呀@ChristopherMorrissey我试过了,但是使用browsershots(一种网站截图服务),我可以在不同的截图中看到相同的访问量,所以它对我不起作用me@BackSlash我已将代码更新为使用fread()
如果它不起作用,请指向您正在使用的实时url。不过,请注意:如果您的Web服务器没有访问counter.txt的权限,则此代码将挂起并可能垃圾邮件发送您的错误日志!fopen()将返回false,您将得到大量的PHP警告:flock()希望参数1是resource
行。。编辑:我编辑了您答案中的代码,以捕获丢失的权限ftruncate()之后必须使用rewind()。所以你的代码目前不起作用。你知道这是否比数据库更好吗?亲爱的上帝,这段代码的缩进发生了什么?
<?php
/**
* Create an empty text file called counterlog.txt and
* upload to the same directory as the page you want to
* count hits for.
*
* Add this line of code on your page:
* <?php include "text_file_hit_counter.php"; ?>
*/
// Open the file for reading
$fp = fopen("counterlog.txt", "r");
// Get the existing count
$count = fread($fp, 1024);
// Close the file
fclose($fp);
// Add 1 to the existing count
$count = $count + 1;
// Display the number of hits
// If you don't want to display it, comment out this line
echo "<p>Page views:" . $count . "</p>";
// Reopen the file and erase the contents
$fp = fopen("counterlog.txt", "w");
fwrite($fp, $count);
// Close the file
fclose($fp);
?>
<?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);
?>
<?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'));
?>
file_put_contents('counter.txt', '1', FILE_APPEND);
echo '<h1>Hi, Page served ' . filesize('counter.txt') . ' times!</h1>';