Я начал играть с PHP и увидел этот фрагмент кода, который должен действовать как счетчик посещений для веб-страницы:
/* counter */
//opens countlog.txt to read the number of hits
$datei = fopen("/countlog.txt","r");
$count = fgets($datei,1000);
fclose($datei);
$count=$count + 1 ;
echo "$count" ;
echo " hits" ;
echo "\n" ;
// opens countlog.txt to change new hit number
$datei = fopen("/countlog.txt","w");
fwrite($datei, $count);
fclose($datei);
Исходя из того, что я прочитал, несколько запросов могут выполняться одновременно на сервере. Так что есть шанс, что они получат доступ к этому файлу countlog.txt
в то же время (правильно?). Если это так, этот код не работает для загруженного веб-сайта (правильно?). Как изменить этот код, чтобы он работал на загруженном веб-сайте? Можете ли вы использовать блокировки в PHP, которые совместно используются несколькими запросами?
PS: вопрос НЕ о счетчиках. Пожалуйста, избегайте использования SQL в ответе, если это возможно.
Вы можете использовать flock (), чтобы получить эксклюзивную блокировку файла, см. http://php.net/manual/en/function.flock.php
$fp = fopen("/tmp/lock.txt", "r+");
if (flock($fp, LOCK_EX)) {
$datei = fopen("/countlog.txt","r");
$count = fgets($datei,1000);
$count=$count + 1 ;
echo "$count" ;
echo " hits" ;
echo "\n" ;
ftruncate($fp, 0);
fwrite($fp, $count);
fflush($fp);
flock($fp, LOCK_UN);
} else {
echo "Could not lock file!";
}
fclose($fp);
Возможно, вам следует создать цикл, который ожидает успешной блокировки, используя usleep (), чтобы избежать активного ожидания: http://php.net/manual/en/function.usleep.php
Я думаю, что ваше требование должно быть выполнено с учетом вашего трафика.
Если ваш трафик низкий, внедрение счетчика на основе блокировки не может быть проблемой. Поскольку вероятность одновременного доступа к одному и тому же файлу очень мала, и для открытия, записи и закрытия файла требуется несколько миллисекунд.
Другим решением может быть использование механизма кэширования memcached, redis или APC и сохранение единого счетчика в хранилище ключей.
Если вы рассматриваете несколько миллионов обращений в секунду, его нельзя разместить на одном сервере. Скорее всего, он масштабируется с помощью балансировщика нагрузки и размещается в разных регионах / серверах. Затем должен быть реализован счетчик обращений неблокированной службы, такой как очередь сообщений. Если вы заинтересованы в очереди вашего счетчика посещений, вы можете прочитать больше на RabbitMQ, или же ActiveMQ
RabbitMQ и ActiveMQ поддерживают следующие протоколы и многие другие протоколы, и вы можете найти множество клиентских библиотек php для подключения по этим протоколам.
Несколько примеров кода
Использование APC в качестве счетчика
<?php
apc_add('counter', 0);
echo apc_inc('counter')
?>
Использование Memcached
<?php
$m = new Memcached();
$m->addServer('localhost', 11211);
$m->add('counter', 0);
$m->increment('counter');
?>
RabbitMQ и php-amqplib
composer.json
{
"require": {
"videlalvaro/php-amqplib": "2.5.*"}
}
$ composer.phar install
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('counter', false, false, false, false);
$callback = function($msg) {
// $msg->body has the content of the message
// counter update implementation goes here
};
$channel->basic_consume('counter', '', false, true, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}
?>