douyue8191 2015-06-30 12:59
浏览 65
已采纳

提供基于IP的冷却时间的便携方式?

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)?

and

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.

  • 写回答

1条回答 默认 最新

  • douershuang7356 2015-06-30 18:55
    关注

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

    Procedure

    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

    <?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);
    ?>
    

    Remarks

    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.

    Memory

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 maixpy训练模型,模型训练好了以后,开发板通电会报错,不知道是什么问题
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 有没有帮写代码做实验仿真的
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥30 vmware exsi重置后登不上
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容