【问题标题】:How to keep this counter from reseting at 100,000? [closed]如何防止此计数器重置为 100,000? [关闭]
【发布时间】:2011-07-12 03:06:50
【问题描述】:

此脚本在 100,000 后重置。我需要更改哪些内容以防止重置并继续计数?

<?php
$filename1 = 'content/general_site_data/total_site_page_loads.txt';

if (file_exists($filename1)) {
    $fh = fopen("content/general_site_data/total_site_page_loads.txt", "a+");
    if($fh==false)
        die("unable to create file");

    $filec = 'content/general_site_data/total_site_page_loads.txt';
    if (!is_writable($filec))
        die('not writable');

    $total_site_page_loads = trim(file_get_contents($filec)) + 1;
    fwrite(fopen($filec, 'w'), $total_site_page_loads);

    echo '------------------------------<br />
    Site Wide Page Views: '.$total_site_page_loads.'<br />';
} else {
    $fh = fopen($filename1, "a");
    $total_site_page_loads = trim(file_get_contents($filename1)) + 1;
    fwrite($fh, $total_site_page_loads);
    fclose($fh);

    echo '------------------------------<br />
    Site Wide Page Views: '.$total_site_page_loads.'<br />';
}
?>

【问题讨论】:

  • 这段代码中没有任何内容会导致重置。
  • 你能举个例子说明当接近 100,000 时计数文件如何变化?
  • 这里没有文件锁定,w模式下第二次打开执行截断。经典的比赛条件,因此可能会重置。答案即将到来。
  • @Charles 但“w”访问似乎仅在文件不存在时才运行
  • @Pekka,事实证明,在执行完代码之后,else 块实际上甚至不应该触发——初始打开处于追加或创建模式,因此文件存在检查不应该除非打开也失败,否则失败,这将导致脚本死机。我已经用更安全的代码更新了我的答案。 (我想这就是我长期使用平面文件数据库所得到的。可怕的回忆......)

标签: php hitcounter


【解决方案1】:

您的代码可能受到a race condition 的影响。

中途,您以w 模式重新打开文件,这会将文件截断为零长度。如果您的脚本的另一个副本打开并尝试在文件被截断但在读取之前读取该文件,则计数器将重置为零。

这是您的代码的更新版本:

    $filename = 'content/general_site_data/total_site_page_loads.txt';
// Open our file in append-or-create mode.
    $fh = fopen($filename, "a+");
    if(!$fh)
        die("unable to create file");
// Before doing anything else, get an exclusive lock on the file.
// This will prevent anybody else from reading or writing to it.
    flock($fh, LOCK_EX);
// Place the pointer at the start of the file.
    fseek($fh, 0);
// Read one line from the file, then increment the number.
// There should only ever be one line.
    $total_site_page_loads = 1 + intval(trim(fgets($fh)));
// Now we can reset the pointer again, and truncate the file to zero length.
    fseek($fh, 0);
    ftruncate($fh, 0);
// Now we can write out our line.
    fwrite($fh, $total_site_page_loads . "\n");
// And we're done.  Closing the file will also release the lock.
    fclose($fh);
    echo '------------------------------',
         '<br />Site Wide Page Views: ',
         $total_site_page_loads,
         '<br />';

因为初始打开是追加或创建模式,所以不需要处理文件不存在的情况,除非初始打开失败。

在文件锁定到位后,无论有多少并发请求,此代码都应该永远重置文件中的计数器。 (当然,除非您碰巧还有其他代码写入该文件。)

【讨论】:

  • 干得好。出于好奇,当锁定到位时会发生什么 - 并发脚本会等待还是跳过?
  • LOCK_EX 是一个阻塞操作,所以脚本会坐下来等待片刻,直到事情被释放。如果锁的所有者在没有释放它的情况下退出,正确的锁实现将释放锁。这取决于操作系统和文件系统,真的。这里有各种各样的边缘情况问题,包括操作系统耗尽文件锁(严重)和如果文件系统是 NFS 挂载,古老的 NFS 版本会搞砸事情,尽管这似乎是真正旧的 Solaris 版本的问题非常可怕的网络主机,通常由电信公司运营。我有时讨厌我以前的工作。
  • “片刻”是指微秒。除非服务器严重缺乏 I/O,否则读取、截断和重写像这样的小文件应该是即时有效的。只有当您收到很多很多并发请求时,您的用户才会注意到延迟。
  • (另外,这正是数据库如此出色的原因。能够说UPDATE Counter SET hits = hits + 1 WHERE site = 'mine' 并让数据库引擎处理并发问题是令人愉快的。)
  • @Charles 很好的解释,谢谢。
【解决方案2】:

我看不到任何重置会发生在哪里,但脚本的工作方式似乎很简单。或许可以尝试将total_site_page_loads.txt 编辑为99990 之类的内容,然后在您切换到100000 时观察该文件会发生什么?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-22
    • 2013-02-08
    • 2015-07-07
    相关资源
    最近更新 更多