8

我有一个网页,我想跟踪它在不使用数据库的情况下被访问了多少次。

我想到了 XML,每次用户访问页面时都会更新一个文件:

<?xml version='1.0' encoding='utf-8'?>
<counter>8</counter>

然后我认为在一个单独的文件中声明一个 PHP 计数器然后在每次用户访问该页面时更新它可能是一个更好的主意。

计数器.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并且update_counter.php文件被 1000 个用户同时访问,则文件会同时被读取 1000 次(因此5在所有请求中都会读取该值)counter.php文件将被更新用值5+1 (=6)代替1005.

有没有办法让它在不使用数据库的情况下工作?

4

5 回答 5

8

您可以使用flock()which 将锁定文件,以便其他进程不会写入文件。

编辑:更新为使用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);
于 2013-08-14T16:17:17.217 回答
7
<?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); 

 ?> 
于 2013-12-12T10:17:16.853 回答
3

听起来很容易,但解决起来真的很难。原因是竞争条件

什么是竞争条件?
如果您打开计数器文件、读取内容、增加点击量并将点击量写入文件内容,那么在所有这些步骤之间可能会发生许多事情,因为其他访问者同时在您的网站上打开相同的脚本。想一想当第一个访问者请求(线程)将“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.2count.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)并查看计数器文件夹以模拟慢速服务器,您会看到多个计数文件的增长速度。

于 2017-03-27T15:48:14.473 回答
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
?>
于 2013-08-14T16:36:40.320 回答
0

除了访问过多的大文件之外,以下工作非常好。

file_put_contents('counter.txt', '1', FILE_APPEND);
echo '<h1>Hi, Page served ' . filesize('counter.txt') . ' times!</h1>';

但是,在文件达到 1000 或 1000000 后,只需创建另一个也计算该单位的文件。大尺寸的不雅与不需要锁定的性能相匹配。

于 2019-01-14T15:26:55.673 回答