我的网站的合法用户偶尔会向服务器发出API请求,导致不希望的结果.我想设定一个上限,不超过每5秒一次API调用或每分钟n次调用(尚未计算出确切的上限).很明显,我可以在数据库中记录每个API调用,并对每个请求进行计算,以查看它们是否超出了限制,但每个请求上的所有额外开销都将达不到目的.我还可以使用哪些资源密集度较低的方法来设定限制?我正在使用PHP/Apache/Linux,这是值得的.
我的网站的合法用户偶尔会向服务器发出API请求,导致不希望的结果.我想设定一个上限,不超过每5秒一次API调用或每分钟n次调用(尚未计算出确切的上限).很明显,我可以在数据库中记录每个API调用,并对每个请求进行计算,以查看它们是否超出了限制,但每个请求上的所有额外开销都将达不到目的.我还可以使用哪些资源密集度较低的方法来设定限制?我正在使用PHP/Apache/Linux,这是值得的.
好的,如果没有any次写入服务器,就无法完成我的请求,但我至少可以消除记录每个请求的功能.一种方法是使用"漏桶"节流方法,它只跟踪最后一个请求($last_api_request
)和时间范围内请求数/限制的比率($minute_throttle
).漏桶从不重置计数器(不像Twitter API的油门每小时重置一次),但如果桶满了(用户达到限制),他们必须等待n
秒,让桶空一点,然后才能发出另一个请求.换句话说,这就像一个滚动限制:如果在时间范围内有以前的请求,它们就会慢慢地从桶中泄漏出来;只有当你把桶装满的时候,它才会限制你.
此代码段将在每个请求上计算一个新的$minute_throttle
值.我在$minute_throttle
中指定了minute,因为你可以在任何时间段添加节流,比如每小时、每天等等...虽然不止一个会很快让用户感到困惑.
$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 );