好吧,對於咯咯我一起扔了一個限制類,可以讓你指定每秒,每分鐘和每小時的限制。我無法拒絕有一個很好的理由來使用循環隊列!
如果您有多個進程正在進行消耗,無論是否同時進行,您都必須設計一種方法來自行存儲和/或共享使用歷史記錄。
// LIMITER.PHP
class Limiter
{
private $queue = array();
private $size;
private $next;
private $perSecond;
private $perMinute;
private $perHour;
// Set any constructor parameter to non-zero to allow adherence to the
// limit represented. The largest value present will be the size of a
// circular queue used to track usage.
// -------------------------------------------------------------------
function __construct($perSecond=0,$perMinute=0,$perHour=0)
{
$this->size = max($perSecond,$perMinute,$perHour);
$this->next = 0;
$this->perSecond = $perSecond;
$this->perMinute = $perMinute;
$this->perHour = $perHour;
for($i=0; $i < $this->size; $i++)
$this->queue[$i] = 0;
}
// See if a use would violate any of the limits specified. We return true
// if a limit has been hit.
// ----------------------------------------------------------------------
public function limitHit($verbose=0)
{
$inSecond = 0;
$inMinute = 0;
$inHour = 0;
$doneSecond = 0;
$doneMinute = 0;
$doneHour = 0;
$now = microtime(true);
if ($verbose)
echo "Checking if limitHit at $now<br>\n";
for ($offset=1; $offset <= $this->size; $offset++)
{
$spot = $this->next - $offset;
if ($spot < 0)
$spot = $this->size - $offset + $this->next;
if ($verbose)
echo "... next $this->next size $this->size offset $offset spot $spot utime " . $this->queue[$spot] . "<br>\n";
// Count and track within second
// -----------------------------
if ($this->perSecond && !$doneSecond && $this->queue[$spot] >= microtime(true) - 1.0)
$inSecond++;
else
$doneSecond = 1;
// Count and track within minute
// -----------------------------
if ($this->perMinute && !$doneMinute && $this->queue[$spot] >= microtime(true) - 60.0)
$inMinute++;
else
$doneMinute = 1;
// Count and track within hour
// ---------------------------
if ($this->perHour && !$doneHour && $this->queue[$spot] >= microtime(true) - 3600.0)
$inHour++;
else
$doneHour = 1;
if ($doneSecond && $doneMinute && $doneHour)
break;
}
if ($verbose)
echo "... inSecond $inSecond inMinute $inMinute inHour $inHour<br>\n";
if ($inSecond && $inSecond >= $this->perSecond)
{
if ($verbose)
echo "... limit perSecond hit<br>\n";
return TRUE;
}
if ($inMinute && $inMinute >= $this->perMinute)
{
if ($verbose)
echo "... limit perMinute hit<br>\n";
return TRUE;
}
if ($inHour && $inHour >= $this->perHour )
{
if ($verbose)
echo "... limit perHour hit<br>\n";
return TRUE;
}
return FALSE;
}
// When an API is called the using program should voluntarily track usage
// via the use function.
// ----------------------------------------------------------------------
public function usage()
{
$this->queue[$this->next++] = microtime(true);
if ($this->next >= $this->size)
$this->next = 0;
}
}
// ##############################
// ### Test the limiter class ###
// ##############################
$psec = 2;
$pmin = 4;
$phr = 0;
echo "Creating limiter with limits of $psec/sec and $pmin/min and $phr/hr<br><br>\n";
$monitorA = new Limiter($psec,$pmin,$phr);
for ($i=0; $i<15; $i++)
{
if (!$monitorA->limitHit(1))
{
echo "<br>\n";
echo "API call A here (utime " . microtime(true) . ")<br>\n";
echo "Voluntarily registering usage<br>\n";
$monitorA->usage();
usleep(250000);
}
else
{
echo "<br>\n";
usleep(500000);
}
}
爲了證明它在行動,我已經把在極限檢查功能有些「詳細模式」的語句。這裏是一些示例輸出。
Creating limiter with limits of 2/sec and 4/min and 0/hr
Checking if limitHit at 1436267440.9957
... next 0 size 4 offset 1 spot 3 utime 0
... inSecond 0 inMinute 0 inHour 0
API call A here (utime 1436267440.9957)
Voluntarily registering usage
Checking if limitHit at 1436267441.2497
... next 1 size 4 offset 1 spot 0 utime 1436267440.9957
... next 1 size 4 offset 2 spot 3 utime 0
... inSecond 1 inMinute 1 inHour 0
API call A here (utime 1436267441.2497)
Voluntarily registering usage
Checking if limitHit at 1436267441.5007
... next 2 size 4 offset 1 spot 1 utime 1436267441.2497
... next 2 size 4 offset 2 spot 0 utime 1436267440.9957
... next 2 size 4 offset 3 spot 3 utime 0
... inSecond 2 inMinute 2 inHour 0
... limit perSecond hit
Checking if limitHit at 1436267442.0007
... next 2 size 4 offset 1 spot 1 utime 1436267441.2497
... next 2 size 4 offset 2 spot 0 utime 1436267440.9957
... next 2 size 4 offset 3 spot 3 utime 0
... inSecond 1 inMinute 2 inHour 0
API call A here (utime 1436267442.0007)
Voluntarily registering usage
Checking if limitHit at 1436267442.2507
... next 3 size 4 offset 1 spot 2 utime 1436267442.0007
... next 3 size 4 offset 2 spot 1 utime 1436267441.2497
... next 3 size 4 offset 3 spot 0 utime 1436267440.9957
... next 3 size 4 offset 4 spot 3 utime 0
... inSecond 1 inMinute 3 inHour 0
API call A here (utime 1436267442.2507)
Voluntarily registering usage
Checking if limitHit at 1436267442.5007
... next 0 size 4 offset 1 spot 3 utime 1436267442.2507
... next 0 size 4 offset 2 spot 2 utime 1436267442.0007
... next 0 size 4 offset 3 spot 1 utime 1436267441.2497
... next 0 size 4 offset 4 spot 0 utime 1436267440.9957
... inSecond 2 inMinute 4 inHour 0
... limit perSecond hit
Checking if limitHit at 1436267443.0007
... next 0 size 4 offset 1 spot 3 utime 1436267442.2507
... next 0 size 4 offset 2 spot 2 utime 1436267442.0007
... next 0 size 4 offset 3 spot 1 utime 1436267441.2497
... next 0 size 4 offset 4 spot 0 utime 1436267440.9957
... inSecond 2 inMinute 4 inHour 0
... limit perSecond hit
Checking if limitHit at 1436267443.5027
... next 0 size 4 offset 1 spot 3 utime 1436267442.2507
... next 0 size 4 offset 2 spot 2 utime 1436267442.0007
... next 0 size 4 offset 3 spot 1 utime 1436267441.2497
... next 0 size 4 offset 4 spot 0 utime 1436267440.9957
... inSecond 0 inMinute 4 inHour 0
... limit perMinute hit
Checking if limitHit at 1436267444.0027
... next 0 size 4 offset 1 spot 3 utime 1436267442.2507
... next 0 size 4 offset 2 spot 2 utime 1436267442.0007
... next 0 size 4 offset 3 spot 1 utime 1436267441.2497
... next 0 size 4 offset 4 spot 0 utime 1436267440.9957
... inSecond 0 inMinute 4 inHour 0
... limit perMinute hit
YOu可以實現一個隊列: 將消息/操作放入隊列中,執行操作直到速率限制命中,解釋限制消息並調整相同類型的隊列消息,以便它們留在隊列中,直到允許您再次處理消息。 –
好問題,沒有簡單的答案。 API中的速率限制可以通過多種方式完成,並且在不同的平臺上有所不同。一些API可以在頭部發送狀態響應,例如Twitter API發送[HTTP 429「Too Many Requests」響應代碼] [1],但除此之外,一般情況下API客戶端很難檢測限制超出限制。 Upvoted這個問題,因爲我期待閱讀其他答案,可能有這個廣泛的主題的智能解決方案/意見。 [1]:https://dev.twitter.com/rest/public/rate-limiting –
謝謝你們兩位,我一直按照你們的建議做@NorbertvanNobelen,但它只是感覺有點原始。例如,Google對其中一些API的每日限制和每秒限制,更多的是「每X秒/分鐘」,這將很好地管理,達到每日限制更多的是軟件設計問題。我假設一個帶有簡單界面的內存後端,在需要時處理暫停? (我想這可能很簡單,我只是不想重新發明輪子!)。 – williamvicary