2015-12-14 30 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(); 
} 
?>