2

我开始使用 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。

4

2 回答 2

3

我认为您的要求应该考虑到您的流量来实现。

如果您的流量很低,那么实现基于锁的计数器就不是问题。因为并发访问同一个文件的概率非常低,打开、写入和关闭文件需要几毫秒。

另一种解决方案可能是使用 memcached、redis 或 APC 缓存机制,并在密钥存储中保留一个计数器。

如果您正在考虑每秒几百万次点击,则它无法托管在单个服务器中。很可能它使用负载均衡器进行扩展并托管在不同的区域/服务器中。然后一个命中计数器应该被实现为非阻塞服务,如消息队列。如果您对排队您的命中计数器感兴趣,您可以在rabbitmqactivemq上阅读更多信息

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

作曲家.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();
}
?>
于 2015-12-14T08:11:37.793 回答
0

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

于 2015-12-14T06:47:03.113 回答