我有一个网页,我想跟踪没有使用数据库的次数。
我考虑过XML,每次用户访问页面时都会更新文件:
<?xml version='1.0' encoding='utf-8'?>
<counter>8</counter>
然后我认为在一个单独的文件中声明一个PHP计数器然后每次用户访问该页面时更新它都是一个更好的主意。
counter.php
<?php
$counter = 0;
?>
update_counter.php:
<?php
include "counter.php";
$counter += 1;
$var = "<?php\n\t\$counter = $counter;\n?>";
file_put_contents('counter.php', $var);
?>
这样,每次访问update_counter.php
时,counter.php
文件中的变量都会递增。
无论如何,我注意到如果counter.php
文件有$counter = 5
并且{1000}用户在同一时间访问update_counter.php
文件,则该文件将被读取1000次同一时间(因此在所有请求中读取值5
)counter.php
文件将使用值5+1 (=6)
而不是1005
进行更新。
有没有办法让它在不使用数据库的情况下工作?
答案 0 :(得分:6)
您可以使用flock()
来锁定文件,以便其他进程不会写入文件。
修改:已更新为使用fread()
而不是include()
$fp = fopen("counter.txt", "r+");
while(!flock($fp, LOCK_EX)) { // acquire an exclusive lock
// waiting to lock the file
}
$counter = intval(fread($fp, filesize("counter.txt")));
$counter++;
ftruncate($fp, 0); // truncate file
fwrite($fp, $counter); // set your data
fflush($fp); // flush output before releasing the lock
flock($fp, LOCK_UN); // release the lock
fclose($fp);
答案 1 :(得分:6)
<?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);
?>
答案 2 :(得分:2)
听起来很简单,但真的难以解决。原因是race-conditions。
什么是竞争条件?
如果您打开计数器文件,阅读内容,增加命中并将命中数写入文件内容,所有这些步骤之间可能会发生很多事情,通过其他访问者同时在您的网站上打开相同的脚本。考虑当第一个访问者请求(线程)将“484049”命中写入char计数器文件时的情况,并且在毫秒内写入“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'));
?>
P.S。启用sleep(3)
并查看计数器文件夹以模拟速度较慢的服务器,您可以看到多个计数文件的增长速度。
答案 3 :(得分:1)
<?php
$File = "counter.txt";
//This is the text file we keep our count in, that we just made
$handle = fopen($File, 'r+') ;
//Here we set the file, and the permissions to read plus write
$data = fread($handle, 512) ;
//Actully get the count from the file
$count = $data + 1;
//Add the new visitor to the count
print "You are visitor number ".$count;
//Prints the count on the page
?>
答案 4 :(得分:0)
以下内容的工作原理很不错,除了文件太多导致访问过多。
file_put_contents('counter.txt', '1', FILE_APPEND);
echo '<h1>Hi, Page served ' . filesize('counter.txt') . ' times!</h1>';
但是,文件达到1000或1000000后,只需创建另一个也计算该单位的文件即可。大尺寸的优雅与不需要锁定的性能相匹配。