防止站点范围的重复提交


Preventing a site-wide double submit

我很难为这个问题找到一个好的标题,所以我希望这是清楚的。我目前正在我的一个网站上使用TwitterOauth模块来发布推文。虽然这有效,但我需要对提交的推文数量设置限制;每小时只有一个

注意:我没有使用数据库的选项。这对于这个问题至关重要。

我已将其合并如下,在处理实际发布到 Twitter API 的 PHP 文件中:

# Save the timestamp, make sure lastSentTweet exists and is writeable
function saveTimestamp(){
    $myFile = "./lastSentTweet.inc";
    $fh = fopen($myFile, 'w');
    $stringData = '<?php function getLastTweetTimestamp() { return '.time().';}';
    fwrite($fh, $stringData);
    fclose($fh);
}
# Include the lastSentTweet time
include('./lastSentTweet.inc');
# Define the delay
define('TWEET_DELAY', 3600);
# Check for the last tweet
if (time() > getLastTweetTimestamp() + TWEET_DELAY) {
    // Posting to Twitter API here
} else {
    die("No.");
}

lastSentTweet.inc文件 (CHMOD 777) 的(初始)内容:

<?php function getLastTweetTimestamp() { return 1344362207;}

问题是,虽然这有效;它允许意外的双重提交;如果多个用户(并且运行此脚本的网站当前非常繁忙)触发此脚本,则碰巧有 2 个提交(或更多,尽管这还没有发生)到 Twitter 溜走,而不仅仅是 1 个。我的第一个想法是打开、写入和关闭文件时(虽然是分钟)延迟,但我可能是错的。

有没有人知道什么允许意外的双重提交(以及如何解决这个问题)?

你得到的是竞争条件。在进行更改时,您需要对文件实施锁定,但您需要将读取(include语句)和更新包含在锁中;关键是确保没有其他人(例如另一个HTTP请求)正在使用该文件,同时读取其当前值,然后使用新的时间戳更新它。

这将是相当无效的。您的 PHP 安装中可能还有其他选项,以下是一些:

  1. 即使您没有数据库服务器,也可以使用数据库:SQLite
  2. 您可以将时间戳存储在 APC 中,并使用 apc_cas() 来检测上次存储的时间戳在更新时是否仍然是最新的。

更新

您的锁定工作流程需要如下所示:

  1. 获取存储时间戳的锁。如果您正在处理文件,则需要打开文件进行读取写入,并对其调用flock()。 如果另一个进程锁定了文件,则flock()将挂起,并且仅在获得锁定后返回,此时尝试锁定文件的其他进程将挂起。
  2. 从已锁定的文件中读取存储的时间戳。
  3. 检查自存储时间戳以来是否已经过去了所需的时间。
    • 当它已过去时,发送推文并将当前时间戳保存到文件中;否则您不会触摸存储的时间戳。
  4. 释放锁(只需关闭文件就足够了)。

这将确保在您读取并测试时间戳之后,但在存储新时间戳之前,没有其他进程会更新时间戳。