如何限制我的网站的API用户?
我的网站的合法用户偶尔会通过API请求攻击服务器,导致不良结果。 我想每隔5秒发一个API调用或每分钟n个调用一个限制(还没有计算出确切的限制)。 我显然可以logging数据库中的每个API调用,并对每个请求进行计算,看看它们是否超出限制,但所有这些额外的开销,每一个请求都将失败的目的。 还有哪些其他资源密集型的方法可以用来制定一个限制? 我正在使用PHP / Apache / Linux,它是值得的。
好吧,没有任何写入服务器的function,我无法做到所要求的事情,但我至less可以消除每一个请求的日志logging。 一种方法是使用“漏桶”调节方法,它只跟踪最后一个请求( $last_api_request
)和时间帧( $minute_throttle
)的请求/限制数量的比率。 漏桶不会重置计数器(不像Twitter API的每小时重置一次),但是如果桶变满(用户达到极限),它们必须等待n
秒才能提出另一个请求。 换句话说,就像一个滚动的限制:如果在这个时间框架内有先前的要求,它们会慢慢地从桶中漏出来。 它只会限制你,如果你填满了水桶。
这个代码片段将会在每个请求上计算一个新的$minute_throttle
值。 我在$minute_throttle
指定了分钟 ,因为您可以为任何时间段(例如小时,每天等)添加节stream阀,尽pipe多个用户可能会很快开始混淆。
$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 );
您可以使用令牌桶algorithm控制速率,这与漏桶algorithm相当。 请注意,您将必须共享stream程(或您想要控制的任何范围)的存储桶状态(即令牌数量)。 所以你可能想考虑locking以避免竞争条件。
好消息是:我为你做了这些: 带宽限制/令牌桶
use bandwidthThrottle\tokenBucket\Rate; use bandwidthThrottle\tokenBucket\TokenBucket; use bandwidthThrottle\tokenBucket\storage\FileStorage; $storage = new FileStorage(__DIR__ . "/api.bucket"); $rate = new Rate(10, Rate::SECOND); $bucket = new TokenBucket(10, $rate, $storage); $bucket->bootstrap(10); if (!$bucket->consume(1, $seconds)) { http_response_code(429); header(sprintf("Retry-After: %d", floor($seconds))); exit(); }
我不知道这个线程是否仍然存在,但我build议将这些统计信息保存在内存caching中,如memcached。 这将减less将请求logging到数据库的开销,但仍然起到这个作用。
最简单的解决scheme是每个API每个24小时只给出一个有限数量的请求,并在一些已知的,固定的时间重置它们。
如果他们耗尽了他们的API请求(即计数器达到零,或者取决于您计算的方向的限制),请停止提供数据,直到您重置计数器。
这样一来,不要求你提出要求,这对他们是最有利的。
你说“每一次请求所有额外的开销都会影响目的”,但我不确定这是否正确。 是不是要防止你的服务器锤击的目的? 这可能是我实现它的方式,因为它只需要快速读/写。 如果您担心性能,您甚至可以将API服务器检查用于其他数据库/磁盘。
但是,如果你想要的替代品,你应该检查mod_cband ,第三方apache模块,旨在协助带宽节stream。 尽pipe主要是为了限制带宽,但它也可以基于每秒请求的速度进行调整。 我从来没有使用它,所以我不知道你会得到什么样的结果。 还有另外一个叫mod-throttle的模块,但是这个项目现在似乎已经closures了,而且从来没有发布过任何超出Apache 1.3系列的东西。
除了从头开始的实现之外,您还可以看一下像3scale( http://www.3scale.net )这样的API基础设施,它可以进行速率限制以及其他一些function(分析等)。 有一个PHP插件: https : //github.com/3scale/3scale_ws_api_for_php 。
你也可以坚持像API的光油,像这样的API速度限制。