Php 提供基于IP的冷却期的便携式方法?

Php 提供基于IP的冷却期的便携式方法?,php,mysql,ip-address,Php,Mysql,Ip Address,我有一个在Web服务器上运行的PHP API前端。这个特定的PHP程序需要分发,因此它应该尽可能具有可移植性 我想要实现的特性是IP冷却期,这意味着同一IP每秒最多只能请求API两次,这意味着至少有500毫秒的延迟 我想到的方法是将IP和最新的请求时间戳一起存储在MySQL数据库中。我通过以下方式获得IP: if (getenv('REMOTE_ADDR')) $ipaddress = getenv('REMOTE_ADDR'); 但是有些服务器可能没有MySQL数据库,或者安装MyS

我有一个在Web服务器上运行的PHP API前端。这个特定的PHP程序需要分发,因此它应该尽可能具有可移植性

我想要实现的特性是IP冷却期,这意味着同一IP每秒最多只能请求API两次,这意味着至少有500毫秒的延迟

我想到的方法是将IP和最新的请求时间戳一起存储在MySQL数据库中。我通过以下方式获得IP:

if (getenv('REMOTE_ADDR'))
    $ipaddress = getenv('REMOTE_ADDR');
但是有些服务器可能没有MySQL数据库,或者安装MySQL数据库的用户没有访问权限。另一个问题是数据库的清理

是否有一种更便携的方法来临时存储IP(记住IPv6)

我如何在性能影响最小的情况下,自动清理超过500毫秒的IP


另外:我对查看存储的IP没有兴趣,只是因为延迟。

这就是我现在使用文件解决问题的方法

程序
  • 获取客户端IP并将其散列(以防止文件读取)
  • 打开IP文件并扫描每一行
  • 将当前记录的时间与当前时间进行比较
  • 如果差异大于设置超时转到5,否则为7
  • 如果IP与客户端匹配,则创建更新的记录,否则
  • 放弃记录
  • 如果IP与客户端匹配,则提供失败消息,否则复制记录
  • 示例代码
    
    
    评论 新用户

    如果IP哈希与任何记录都不匹配,则会创建一个新记录。注意:如果您没有访问权限,访问可能会失败

    内存

    如果您希望获得更多流量,请切换到类似all together的数据库解决方案

    冗余代码


    “但是minxomat”,您可能会说,“现在每个客户机都在整个文件中循环!”。是的,确实如此,这就是我想要它作为我的解决方案的方式。这样,每个客户端都负责清理整个文件。即使如此,对性能的影响仍然很小,因为如果每个客户端都在清理,文件大小将保持在绝对最小。如果这种方法不适合您,请更改此选项。

    查看。它提供了一种非常方便的方法。@Andrew谢谢,这有助于创建我自己的基于文件的解决方案!你在那里有比赛条件。。。两个PHP请求将尝试添加其自己的IP,但当第一个请求正在清理时,第二个请求将其IP写入文件。然后,第1个将替换为以前读取的版本(不包含第2个IP)。当您得到更多的请求时,它将是均匀的worse@Marki555这真的是个大问题吗?更新/删除记录时会重新加载文件,因此冲突必须在这么短的时间内发生,还是我遗漏了什么?取决于。。如果您有500个请求/秒,那么10秒的IP值将为5000。比较长字符串散列是低效的(与存储为32位数字的IP相比),例如4ms。这意味着每次你都会有两个同时运行。如果你需要担心客户端每秒访问超过两次,相信我,你也需要担心比赛条件。您的基本方法看起来有效,但与代码一起使用,因此您只使用“标准”函数(即no
    file\u put\u contents
    etc),并确保
    flock($nFileHandle,LOCK\u EX)就在您的
    fopen
    呼叫和
    flock($nFileHandle,LOCK_UN)之后在任何
    fclose
    调用之前。无论“清理”循环是否找到IP(即关闭句柄之前写入新IP)@Marki555,您也应该将文件缩减为只打开一次。一个成功的API请求可能需要120秒,所以在一些客户端清理时锁定文件应该不是问题,因为即使2秒的延迟对我来说也可以忽略不计。
    <?php
    
    $sIPHash    = md5($_SERVER[REMOTE_ADDR]);
    $iSecDelay  = 10;
    $sPath      = "bucket.cache";
    $bReqAllow  = false;
    $iWait      = -1;
    $sContent   = "";
    
    if ($nFileHandle = fopen($sPath, "c+")) {
        flock($nFileHandle, LOCK_EX);
        $iCurLine = 0;
        while (($sCurLine = fgets($nFileHandle, 4096)) !== FALSE) {
            $iCurLine++;
            $bIsIPRec = strpos($sCurLine, $sIPHash);
            $iLastReq = strtok($sCurLine, '|');
            // this record expired anyway:
            if ( (time() - $iLastReq) > $iSecDelay ) {
                // is it also our IP?
                if ($bIsIPRec !== FALSE) {
                    $sContent .= time()."|".$sIPHash.PHP_EOL;
                    $bReqAllow = true;
                }
            } else {
                if ($bIsIPRec !== FALSE) $iWait = ($iSecDelay-(time()-$iLastReq));
                $sContent .= $sCurLine.PHP_EOL;
            }
        }
    }
    
    if ($iWait == -1 && $bReqAllow == false) {
        // no record yet, create one
        $sContent .= time()."|".$sIPHash.PHP_EOL;
        echo "Request from new user successful!";
    } elseif ($bReqAllow == true) {
        echo "Request from old user successful!";
    } else {
        echo "Request failed! Wait " . $iWait . " seconds!";
    }
    
    ftruncate($nFileHandle, 0);
    rewind($nFileHandle);
    fwrite($nFileHandle, $sContent);
    flock($nFileHandle, LOCK_UN);
    fclose($nFileHandle);
    ?>