如何限制网站的API用户?

时间:2009-09-03 19:39:46

标签: php linux apache throttling

我网站的合法用户偶尔会使用导致不良结果的API请求来破坏服务器。我想建立一个限制,不过每5秒说一次API调用或每分钟n次调用(还没有弄清楚确切的限制)。我显然可以在数据库中记录每个API调用,并对每个请求进行计算以查看它们是否超出限制,但是每个请求的所有额外开销都将失去目的。我可以用什么其他资源不足的方法来制定限制?我正在使用PHP / Apache / Linux,这是值得的。

6 个答案:

答案 0 :(得分:51)

好的,没有办法在没有任何写入服务器的情况下执行我的要求,但我至少可以消除每个请求的记录。一种方法是使用“漏桶”限制方法,它只跟踪上一个请求($last_api_request)和时间帧的请求数/限制比($minute_throttle) 。泄漏的存储桶永远不会重置其计数器(不同于每小时重置的Twitter API节流),但如果存储桶已满(用户达到限制),则必须等待n秒才能将存储桶清空一点可以提出另一个请求。换句话说,它就像一个滚动限制:如果在时间范围内有先前的请求,它们就会慢慢泄漏出来;它只会限制你填充桶。

此代码段将在每个请求中计算新的$minute_throttle值。我在$minute_throttle中指定了分钟,因为您可以为任何时间段添加限制,例如每小时,每天等等...虽然不止一个会很快开始让它混淆用户。

$minute = 60;
$minute_limit = 100; # users are limited to 100 requests/minute
$last_api_request = $this->get_last_api_request(); # get from the DB; in epoch seconds
$last_api_diff = time() - $last_api_request; # in seconds
$minute_throttle = $this->get_throttle_minute(); # get from the DB
if ( is_null( $minute_limit ) ) {
    $new_minute_throttle = 0;
} else {
    $new_minute_throttle = $minute_throttle - $last_api_diff;
    $new_minute_throttle = $new_minute_throttle < 0 ? 0 : $new_minute_throttle;
    $new_minute_throttle += $minute / $minute_limit;
    $minute_hits_remaining = floor( ( $minute - $new_minute_throttle ) * $minute_limit / $minute  );
    # can output this value with the request if desired:
    $minute_hits_remaining = $minute_hits_remaining >= 0 ? $minute_hits_remaining : 0;
}

if ( $new_minute_throttle > $minute ) {
    $wait = ceil( $new_minute_throttle - $minute );
    usleep( 250000 );
    throw new My_Exception ( 'The one-minute API limit of ' . $minute_limit 
        . ' requests has been exceeded. Please wait ' . $wait . ' seconds before attempting again.' );
}
# Save the values back to the database.
$this->save_last_api_request( time() );
$this->save_throttle_minute( $new_minute_throttle );

答案 1 :(得分:8)

您可以使用token bucket algorithm来控制速率,这与漏桶算法相当。请注意,您必须在进程(或您想要控制的任何范围)上共享存储桶的状态(即令牌数量)。因此,您可能需要考虑锁定以避免竞争条件。

好消息:我为你做了所有这些:bandwidth-throttle/token-bucket

repaint()

答案 2 :(得分:4)

我不知道这个线程是否仍然存在,但我建议将这些统计信息保存在内存缓存中,如memcached。这将减少将请求记录到数据库的开销,但仍然可以达到目的。

答案 3 :(得分:3)

最简单的解决方案是每24小时为每个API密钥提供有限数量的请求,并在某个已知的固定时间重置它们。

如果他们耗尽了他们的API请求(即计数器达到零或极限,取决于您计算的方向),请停止为他们提供数据,直到您重置他们的计数器。

通过这种方式,最好的利益就是他们的最好的利益,不要抨击你的请求。

答案 4 :(得分:1)

你说“在每个请求上所有额外的开销都会打败目的”,但我不确定这是否正确。是不是要防止锤击您的服务器?这可能是我实现它的方式,因为它实际上只需要快速读/写。如果您担心性能,甚至可以将API服务器检查分配到不同的数据库/磁盘。

但是,如果您需要替代方案,则应该查看mod_cband,这是一个旨在协助带宽限制的第三方apache模块。尽管主要用于带宽限制,但它也可以根据每秒请求进行限制。我从来没有用过它,所以我不确定你会得到什么样的结果。还有一个名为mod-throttle的模块,但该项目现在似乎已关闭,并且从未发布过Apache 1.3系列以上的任何内容。

答案 5 :(得分:1)

除了从头开始实施之外,您还可以查看像3scale(http://www.3scale.net)这样的API基础架构,它可以进行速率限制以及其他一些东西(分析等)。有一个PHP插件:https://github.com/3scale/3scale_ws_api_for_php

您还可以使用类似于Varnish的API,并像这样限制API速率。