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