2015-12-14 96 views
2

我开始用PHP玩,只见那应该作用命中计数器网页此代码段: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。

回答

0

你可以使用的羊群(),以获取有关文件的排它锁,见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

+0

根据该文档,羊群将阻塞,直到该锁被获取。为什么你建议使用循环(假设一个while循环)? – Kasra

+0

对不起,没有提到:我通常使用flock()和Option LOCK_NB。这给了我避免死锁的机会。例如。人们只能尝试锁定100次然后放弃。 – maxhb

2

我认为你的要求应该考虑到你的流量来实施​​。

如果您的流量很低,实施基于锁的计数器可能不成问题。由于并发访问同一文件的概率非常低,打开,写入和关闭文件需要几个毫秒。

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