2015-07-02 16 views
18

Tôi đã tìm thấy rất nhiều thông tin và các ví dụ về tập lệnh cho thấy cách xếp hạng giới hạn người dùng API nhưng tôi không thể tìm thấy bất kỳ ví dụ nào về cách xếp hạng giới hạn yêu cầu của riêng bạn một API khi các giới hạn này được áp đặt.Tỷ lệ giới hạn bản thân do quá tải API bên ngoài

Tôi luôn xếp hạng giới hạn các tập lệnh với mã như lệnh sleep hoặc usleep nhưng cảm thấy như một cách không hiệu quả khi làm việc, đặc biệt là khi các điểm cuối API có giới hạn tốc độ khá cao và búa cho đến khi bạn đạt đến giới hạn cũng không hiệu quả. Ví dụ: giới hạn API của Google thay đổi dựa trên API bạn đang sử dụng và có thể tăng/giảm, trong trường hợp này, giới hạn tỷ lệ cố định được mã hóa cứng vào mã có vẻ như công việc đoán nguyên thủy!

Tôi đã bỏ lỡ điều gì đó khá rõ ràng? Hay điều này không phổ biến như tôi mong đợi?

+0

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ư . –

+0

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 –

+0

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

Trả lời

3

Đượ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 
+0

Nhân tiện, tôi có điều này trên Github (với giấy phép MIT vô cùng mở) trong trường hợp bất cứ ai muốn sử dụng mã hoàn toàn trong một dự án nhưng đang ở trong một môi trường với các quy định nghiêm ngặt về cấp phép rõ ràng. https://github.com/gvroom/snippets –

4

Trước tiên, bạn nên gọi bất kỳ API bên ngoài nào khi bạn thực sự cần - các nhà cung cấp sẽ cảm ơn bạn một cách đáng yêu.

Có hai cách tôi thường "áp đặt" giới hạn sử dụng API của chính mình - nếu có thể, hãy lưu kết quả trong khoảng thời gian N, thường ít hơn rất nhiều so với giới hạn cứng của chính API đó. Điều này, tuy nhiên, chỉ hoạt động trong trường hợp rất cụ thể.

Bộ đếm thứ hai là bộ đếm liên tục/bán liên tục, nơi bạn lưu trữ bộ đếm trong một số loại bộ nhớ phụ trợ cùng với thời gian khi khoảng thời gian giới hạn bắt đầu. Mỗi lần trước khi gọi API, hãy kiểm tra bộ nhớ và xem liệu khoảng thời gian trừ đi hiện tại có bắt đầu hay không và số lượng yêu cầu bạn đã thực hiện ít hơn được áp đặt bởi API. Nếu có, bạn có thể yêu cầu - nếu khoảng thời gian lớn hơn, bạn có thể đặt lại giới hạn và nếu yêu cầu tiếp theo của bạn vượt quá giới hạn và bạn vẫn ở trong khoảng thời gian trước, bạn có thể hiển thị lỗi khá. Trên mỗi yêu cầu bên ngoài, sau đó cập nhật khoảng thời gian nếu nó vượt quá và tăng bộ đếm.

0

Tôi nghĩ chúng tôi không thể trả lời câu hỏi của bạn trong một vài câu. Nó phản ánh đúng kiến ​​trúc liên kết với ứng dụng của bạn. Đối với tôi để tạo giới hạn tốc độ API lặp lại, tôi sử dụng bộ nhớ cache lưu trữ các giá trị và sử dụng API của tôi. Tôi phải hẹn hò không tìm thấy mã nào.

Các vấn đề liên quan