2015-06-30 12:59
浏览 65


I have a PHP API front end running on a webserver. This specific PHP program is subject to distribution, thus it should be as portable as possible.

The feature I want to implement is an IP cooldown period, meaning that the same IP can only request the API a maximum of two times per second, meaning at least a 500ms delay.

The approach I had in mind is storing the IP in an MySQL database, along with the latest request timestamp. I get the IP by:

if (getenv('REMOTE_ADDR'))
    $ipaddress = getenv('REMOTE_ADDR');

But some servers might not have a MySQL database or the user installling this has no access. Another issue is the cleanup of the database.

Is there a more portable way of temporarily storing the IPs (keeping IPv6 in mind)?


How can I provide an automatic cleanup of IPs that are older than 500ms, with the least possible performance impact?

Also: I have no interest at looking at stored IPs, it is just about the delay.

图片转代码服务由CSDN问答提供 功能建议

我在网络服务器上运行了一个PHP API前端。 这个特定的PHP程序需要分发,因此它应该尽可能便携。

我想要实现的功能是IP冷却时间段,这意味着同一个IP只能请求 API最多每秒两次,意味着至少延迟500ms。

我想到的方法是将IP存储在MySQL数据库中,以及最新的请求时间戳。 我得到了IP:

 $ ipaddress = getenv('REMOTE_ADDR'); 

但是有些服务器可能没有MySQL数据库,或者安装此服务器的用户无权访问。 另一个问题是清理数据库。



另外:我没兴趣 在查看存储的IP时,这只是延迟。

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • douershuang7356 2015-06-30 18:55

    This is how I solved it for now, using a file.


    1. Get client IP and hash it (to prevent file readout).
    2. Open IP file and scan each line
    3. Compare the time of the current record to the current time
    4. If difference is greater than set timeout goto 5., else 7.
    5. If IP matches client, create updated record, else
    6. drop record.
    7. If IP matches client, provide failure message, else copy record.

    Example code

    $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) {
            $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);
    fwrite($nFileHandle, $sContent);
    flock($nFileHandle, LOCK_UN);


    New users

    If the IP hash doesn't match any record, a new record is created. Attention: Access might fail if you do not have rights to do that.


    If you expect much traffic, switch to a database solution like this all together.

    Redundant code

    "But minxomat", you might say, "now each client loops through the whole file!". Yes, indeed, and that is how I want it for my solution. This way, every client is responsible for the cleanup of the whole file. Even so, the performance impact is held low, because if every client is cleaning, file size will be kept at the absolute minimum. Change this, if this way doesn't work for you.

    点赞 打赏 评论

相关推荐 更多相似问题