首页 > 解决方案 > API调用尊重使用延迟计数器的速率限制,如何?javascript 谷歌应用脚​​本

问题描述

我有大约 50k 行数据需要通过 API 服务进行查询。该服务将其 API 调用限制如下:

我现在专注于每秒和每分钟的限制,循环遍历所有行并在速率限制超过时停止:

var app = SpreadsheetApp;
var sheet = app.getActiveSpreadsheet().getSheetByName("data");
var maxRows = sheet.getLastRow();
var maxColumns = sheet.getLastColumn();
var data = sheet.getrange(2,1,maxRows,maxColumns).getValues();

var rateLimitSec = 50;
var rateLimitMin = 2500;
var rateLimitSecUsed = 0;
var rateLimitMinUsed = 0;

function example () {
    for ( 
    i=0; 
    (i<=maxRows) || (rateLimitSecUsed<rateLimitSec) || (rateLimitMinUsed<=RateLimitMin);
    i++
  )
    { // start loop code
            // do the API request
      rateLimitSecUsed = rateLimitSecUsed+1 // and increase Rate limit used marker
      rateLimitMinUsed = rateLimitMinUsed+1
      
      if ( !(rateLimitMinUsed < RateLimitMin) ) {
        // stop for now, or add better solution later. show some warning;
      } else if ( !( rateLimitSecUsed !< rateLimitSec) ) {

-->>    Utilities.sleep(1000); // HERE I WANT A DELAY FOR 1S, <<---

        rateLimitSec = 0; // reset limit after delay.
        example(); // call function again after delay
      }
      
    } // end loop code
}; // end function example

但是,这并没有按预期工作。我在某处读到,由于异步性,这将不起作用,并且我需要使用类 lock。但是,我无法弄清楚锁类是如何工作的。任何人都可以帮助指导我正确的方向吗?锁类是正确的解决方案吗?如果是,它是如何工作的?

另外,我控制速率限制的方法是好还是有更好的方法呢?

这应该是一个很常见的问题,但我找不到任何像样的例子。

编辑:在示例代码中发现了一些错字,修复了我发现的内容。不要太担心这一切,问题的重点是“我如何在一个循环和下一个循环之间添加延迟,以确保我不会超过 API 限制?”

标签: javascriptgoogle-apps-scriptgoogle-sheetsrate-limiting

解决方案


我会以不同的方式处理这个逻辑——一种更简单的方式,它避免了您当前正在使用的递归调用(example()从内部调用example())。

假设您有 25,000 到 50,000 行数据要处理。这意味着您需要保持在“每天 50,000 次”的限制内 - 但您不需要将这些呼叫分散到整个 24 小时内。您可以将它们分散在 2 个小时内,以避免“每小时 25,000 个”的限制。

即:7,200 秒内最多调用 25,000 次 API,或每 0.144 秒调用不超过 1 次。让我们称之为我们的“节流值”。我们甚至可以稍微增加这个数字以确保我们安全地处于允许的限制范围内:每 0.2 秒调用 1 次,即每小时 18,000 次调用(或任何适合您的安全边际)。

在这种情况下,我们可以使用如下代码:

var maxRows = 48987; // calculated from the spreadsheet
var throttle = 200; // millis

function example() {
  for (i = 1; i <= maxRows; i++) {
    // do your API call here
    Utilities.sleep(throttle);
  }
}

现在,如果您有 50,000 到 100,000 行数据要处理,则需要不同的“节流值”计算。

每天 50,000 次呼叫 = 每 86,400 秒 50,000 次呼叫 = 每 1,728 毫秒 1 次呼叫(同样,您可以调整安全性 - 比如说 1,800 毫秒。

您可以修改上面的代码来解决这个问题:

var maxRows = ...; // calculated from the spreadsheet
var throttle;

if (maxRows <= 100000 && maxRows > 50000) {
  throttle = 1800;
} else if (maxRows <= 50000 && maxRows > 25000) {
  throttle = 200;
} // and more else/if statements as needed for different row counts...

function example() {
  for (i = 1; i <= maxRows; i++) {
    // do your API call here
    Utilities.sleep(throttle);
  }
}

您当然可以通过使用 switch 语句来获得更多的节流值,并使用变量而不是那些硬编码的数字来改进这一点——但这给了你这个想法。


这故意不是吞吐量优化的解决方案。它以简单为代价进行优化。

通过在处理初始数据块后重新评估“剩余”行数,您可以获得更好的吞吐量。例如,如果您有 50,123 行数据,那么您可以对前 50,000 行使用一个限制值,然后对剩余的 123 行使用一个更好的限制值。

它也没有考虑您的流程开始的一天中的时间(我不知道这里的“一天”是什么 - 可能是 UTC 午夜到 UTC 午夜。)但是有可能根据开始时间进行进一步优化, 还。

我会保持简单。


推荐阅读