【问题标题】:Visits counter without database with PHP使用 PHP 访问没有数据库的计数器
【发布时间】:2013-08-14 15:56:31
【问题描述】:

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

我想到了 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 并且 update_counter.php 文件被 1000 个用户同时访问,则该文件同时被读取 1000 次(所以值 5 在所有请求中被读取)counter.php 文件将更新为值 5+1 (=6) 而不是 1005

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

【问题讨论】:

  • 你会想看看flock()php.net/manual/en/function.flock.php
  • @ChristopherMorrissey 我真的不知道这个功能,你能解释一下它是什么以及如何在答案中使用它吗?
  • 我认为要使用flock(),您必须阅读文件而不是包含它。它会添加一些代码,但不会太难。问题可能在于您必须在锁定文件之前打开文件,并且可能有多个用户在获得锁之前打开它(意味着多个用户将读取相同的初始值)
  • @MichaelWheeler 如果文件被锁定,这是否意味着如果文件被打开,另一个脚本打开它的请求被拒绝?还是其他脚本只会等待锁被释放?
  • 添加这个作为答案,因为我没有足够的声誉来评论 - 实际上在 cmorrissey 的答案中的 ftruncate() 之后,需要插入以下行以确保在开始时写入文件。否则,无关的空值将阻止所需的结果。倒带($fp);

标签: php counter


【解决方案1】:

您可以使用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);

【讨论】:

  • @MichaelWheeler 哎呀!
  • @ChristopherMorrissey 我试过了,但是使用 browsershots(网站截图服务)我可以在不同的截图中看到相同的访问量,所以它不适合我
  • @BackSlash 我已更新代码以使用 fread() 如果它不起作用将我们指向您正在使用的实时网址
  • 请注意:如果您的网络服务器没有访问 counter.txt 的权限,此代码将挂起并可能向您的错误日志发送垃圾邮件! fopen() 将返回 false,您将获得很多 PHP Warning: flock() expects parameter 1 to be resource 行。编辑:我在您的答案中编辑了代码以捕获丢失的权限
  • 您必须在 ftruncate() 之后使用 rewind()。所以你的代码目前不工作。
【解决方案2】:
<?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); 

 ?> 

【讨论】:

  • 亲,这段代码的缩进是怎么回事?
【解决方案3】:

这听起来很容易,但解决起来真的很难。原因是race-conditions

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

附:启用sleep(3) 并查看计数器文件夹以模拟慢速服务器,您会看到多个计数文件的增长速度。

【讨论】:

    【解决方案4】:
    <?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
    ?>
    

    【讨论】:

      【解决方案5】:

      除了文件太大,访问次数过多之外,以下内容效果很好。

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

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

      【讨论】:

      • 你知道这是否会胜过数据库吗?
      猜你喜欢
      • 1970-01-01
      • 2018-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-12
      • 2017-09-02
      相关资源
      最近更新 更多