【问题标题】:PHP multiple clients accessing same filePHP多个客户端访问同一个文件
【发布时间】:2016-03-19 13:58:15
【问题描述】:

我开始使用 PHP,看到了这段代码 sn-p,它应该充当网页的命中计数器:

/* 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。

【问题讨论】:

    标签: php hitcounter


    【解决方案1】:

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

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

    另一种解决方案可能是使用 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

    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();
    }
    ?>
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 根据文档,flock 将阻塞直到获得锁。你为什么建议使用循环(假设是一个while循环)?
      • 对不起,没有提到这个:我通常使用flock()和Option LOCK_NB。这让我有机会避免死锁。例如。只能尝试获取锁 100 次然后放弃。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多