Được rồi, để cười khúc khích tôi đã cùng nhau đưa ra một lớp giới hạn cho phép bạn chỉ định giới hạn trên giây, phút và giờ. Tôi không thể cưỡng lại có lý do chính đáng để sử dụng hàng đợi tròn!
Nếu bạn có nhiều quy trình thực hiện việc tiêu thụ, dù có đồng thời hay không, bạn sẽ phải nghĩ ra cách lưu trữ và/hoặc chia sẻ lịch sử sử dụng của riêng bạn.
// 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);
}
}
Để minh họa nó trong thực tế, tôi đã đưa vào một số câu lệnh "tiết chế độ" trong chức năng kiểm tra giới hạn. Đây là một số đầu ra mẫu.
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
Nguồn
2015-07-07 11:21:02
YOu có thể thực hiện hàng đợi: Đặt thông điệp/hành động trong hàng đợi, thực hiện hành động cho đến khi đạt đến giới hạn tốc độ, diễn giải thông báo giới hạn và điều chỉnh thư được xếp hàng cùng loại để chúng ở trong hàng đợi cho đến khi bạn được phép xử lý lại thư . –
Câu hỏi hay, không có câu trả lời đơn giản. Giới hạn tốc độ trong API: s có thể được thực hiện theo nhiều cách, và khác nhau trên các plattforms khác nhau. Một số API: s có thể gửi phản hồi trạng thái trong tiêu đề, ví dụ: API Twitter gửi mã phản hồi [HTTP 429 “Quá nhiều yêu cầu”] [1], nhưng nói cách khác và nói chung, các khách hàng API khó phát hiện khi vượt quá giới hạn . Đã bỏ phiếu cho câu hỏi, vì tôi rất mong được đọc các câu trả lời khác có thể có giải pháp/nhận xét thông minh về chủ đề rộng này. [1]: https://dev.twitter.com/rest/public/rate-limiting –
Cảm ơn cả hai, tôi đã thực hiện lịch sử như bạn đề xuất @NorbertvanNobelen nhưng nó chỉ cảm thấy một chút nguyên thủy. Ví dụ: Google có giới hạn hàng ngày và giới hạn trên mỗi giây đối với một số API của nó, đó là "trên X giây/phút" tốt hơn để quản lý, nhấn giới hạn hàng ngày là vấn đề thiết kế phần mềm. Tôi giả sử một phụ trợ trong bộ nhớ với một giao diện đơn giản mà xử lý tạm dừng nếu/khi cần thiết? (Tôi đoán nó có thể được khá đơn giản, tôi chỉ không muốn phát minh lại bánh xe!). – williamvicary