PHP 多个客户端访问同一个文件
PHP multiple clients accessing same file
我开始玩 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 缓存机制,并在密钥库中保留单个计数器。
如果您考虑每秒几百万次点击,则无法将其托管在单个服务器中。它很可能是通过负载均衡器进行扩展并托管在不同的 regions/servers 中。然后应该像消息队列一样实现非阻塞服务的命中计数器。如果您对排队计数器感兴趣,可以在 rabbitmq, or activemq
上阅读更多内容
RabbitMQ 和 ActiveMQ 支持以下协议和许多其他协议,您可以找到许多 php 客户端库来连接这些协议。
- AMQP - php-amqplib、
- 践踏 - reactphp/stomp, stomp-php, ZendQueue
少量代码示例
使用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();
}
?>
我开始玩 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 缓存机制,并在密钥库中保留单个计数器。
如果您考虑每秒几百万次点击,则无法将其托管在单个服务器中。它很可能是通过负载均衡器进行扩展并托管在不同的 regions/servers 中。然后应该像消息队列一样实现非阻塞服务的命中计数器。如果您对排队计数器感兴趣,可以在 rabbitmq, or activemq
上阅读更多内容RabbitMQ 和 ActiveMQ 支持以下协议和许多其他协议,您可以找到许多 php 客户端库来连接这些协议。
- AMQP - php-amqplib、
- 践踏 - reactphp/stomp, stomp-php, ZendQueue
少量代码示例
使用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();
}
?>