Счетчик посещений без базы данных с PHP

у меня есть одна веб-страница, и я хотел бы отслеживать, сколько раз он посетил без использования базы данных.

я думал о 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 = "<?phpnt$counter = $counter;n?>";
    file_put_contents('counter.php', $var);
?>

С это, каждый раз update_counter.php посещается, переменная в увеличивается.

во всяком случае, я заметил, что если и $counter = 5 и update_counter.php файл посещается т. е. 1000 пользователей в то же самое время, файл читается 1000 раз в то же время (так что значение 5 читается во всех запросах)counter.php файл будет обновлен со значением 5+1 (=6) вместо 1005.

есть ли способ заставить его работать без использования база данных?

4 ответов


можно использовать flock() который заблокирует файл, чтобы другие процессы не записывали в файл.

Edit: обновлено для использования 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);

<?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 
 $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
?>

это звучит легко, но его очень трудно решить. Причина в расы-условия.

каковы расовые условия?
Если вы открываете файл счетчика, читаете содержимое, увеличиваете хиты и записываете хиты в содержимое файла, многое может произойти между всеми этими шагами через других посетителей, открывающих один и тот же скрипт на вашем сайте одновременно. Подумайте о ситуации, когда первый запрос посетителей (поток) пишет" 484049 " попадает в счетчик файла char по char и в миллисекунду, пока" 484 "записывается, второй поток считывает это значение и увеличивает его до" 485", теряя большинство ваших хороших хитов.

не используйте глобальные блокировки!
Возможно, вы думаете о решении этой проблемы с помощью LOCK_EX. При этом второй поток должен подождать, пока первый не закончит запись в файл. Но "ждать" - это не то, чего вы действительно хотите. Это означает каждую нить, и я действительно имею в виду, что каждая нить нуждается ждать других потоков. Вам нужны только некоторые бушующие боты на вашем сайте, много посетителей или временная проблема ввода-вывода на вашем диске, и никто не может загрузить ваш сайт, пока все записи не будут закончены... а что будет, если посетитель не сможет открыть ваш сайт... он обновит его, вызывая новые потоки ожидания / блокировки... бутылочное горлышко!

использовать блокировки на основе потоков
Единственное безопасное решение-мгновенно создать новый файл счетчика для одновременного запуск потоков:

<?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, etc в вашей встречной папке пока 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) и посмотрите в папку счетчика, чтобы имитировать медленный сервер, и вы увидите, как быстро растут несколько файлов подсчета.