当我使用google-api-nodejs-client调用Calendar API时,遇到了超过速率限制的问题(403),然后进行了重试
总结
在使用 google-api-nodejs-client 创建一个自定义 API 来调用 Calendar API 时,由于达到一定的访问量限制,出现了“超出速率限制”的错误。因此,我想记录一下可能导致此错误的原因以及一些缓解策略。
※ Rate limit など、Google に関する API の仕様などは常に変動する可能性があります。
この記事の情報は2023/06/30時点の情報になります。
※ 当人は Node.js もgoogleのAPI についても初学者です??♂️
发生了什么事情?
我已经在概要中写过了,我使用google-api-nodejs-client创建了一个使用Calendar API创建事件的自定义API。但是当API的访问量超过一定限制时,会发生以下错误:
GaxiosError: Rate Limit Exceeded
at Gaxios._request (/app/node_modules/gaxios/build/src/gaxios.js:130:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async JWT.requestAsync (/app/node_modules/google-auth-library/build/src/auth/oauth2client.js:382:18) {
[stack]: 'Error: Rate Limit Exceeded\n' +
' at Gaxios._request (/app/node_modules/gaxios/build/src/gaxios.js:130:23)\n' +
' at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n' +
' at async JWT.requestAsync (/app/node_modules/google-auth-library/build/src/auth/oauth2client.js:382:18)',
[message]: 'Rate Limit Exceeded',
正如前面所述,如果进一步查看错误对象,还可以发现以下部分,可以看出已经触发了速率限制。
code: 403,
errors: [
{
domain: 'usageLimits',
reason: 'rateLimitExceeded',
message: 'Rate Limit Exceeded'
},
[length]: 1
]
Google日历API中关于Late的规范
当查看官方网站时,发现如下信息。
为了保护Google日历的用户、管理员和组织免受不正行为的侵害,Google日历设定了使用限制。
在将免费账户切换为付费账户后的60天内, 日历的使用上限将提升,允许进行更多的操作。
而且,在以下也有这样的记录,但是在这次事情中,我是用付费账户的信息进行测试的。
在Google中,为了保护用户免受垃圾邮件的侵害,我们不公开适用更严格限制的确切上限。付费订阅帐户的上限明显高于以下帐户:
– 在达到付费标准后的60天内的付费订阅
– 试用账户
– 传统免费版G Suite用户
– Google for Nonprofits | 非营利组织计划客户
进一步查看官方说明如下记载。
將試用版的上限更改為訂閱帳戶上限
即使切換為付費帳戶,使用量上限也不會自動更改。請按照以下步驟進行處理。購買域名。
支付超過100美元的總計費用,超過域名費用。詳細信息請參閱手動(提前)支付。
等待60天。
然後,您可以提高上限。
也就是说,除非支付了累计100美元以上的索赔金额,否则上限将不会取消,并且可能会施加严格的限制。
速率限制的具体值。
如果试图对其进行适当的负载并验证,我们发现严格限制大约在每秒5个请求左右发生。
关于这一点,当我在论坛上搜索是否有相关文档时,虽然回答有些过时,但有人这样回答。
在文件中没有提到,但是Calendar API(以及其他API)对每个用户的API请求有限制,请注意。
Calendar API的默认请求限制是每秒5次/用户。
另外,在查看谷歌开发者博客时,附上了下面的图片。从图片上看,似乎是每秒5个请求。
对策
以下是关于 google-api-nodejs-client的缓解措施。虽然应对方法根据API的用途可能会有所不同,但就我的情况而言,是这样的。
-
- あくまで検証なので 5req/sec は辛いが、最大でも20req/sec 程度に耐えれれば十分
-
- 処理はある程度遅延しても問題ない
- 制約上ユーザーを増やせばスケールするが、管理上ユーザーを増やすのは面倒
根据以上,我尝试找到了重新尝试的方针。
Google API Node.js客户端的重试相关机制。
首先,我找到了关于支持googleapis-node-js-client的Retry的问题Support for Retry for googleapis-node-js-client #1510。
问候!目前我们默认不支持重试。不过我很乐意调查一下!在这段时间,我建议你可以考虑使用类似于p-retry的方案。
实际上,这在#482中已经涵盖了!将此标记为重复问题并关闭。
在查看上述内容时似乎没有得到相应,但是继续确认第482个问题后发现,与之相关的拉取请求“feat: retry requests by default #104”已经被协调了。
当查看了Pull Request时,似乎解决了重试问题。
重大变更:此变更默认启用HTTP重试功能。重试逻辑与gaxios的默认设置相匹配。
修复了googleapis/google-api-nodejs-client#482。
另外,关于两个仓库的相关性,以下是一个示例,根据查看package-lock.json文件可以看出,google-api-nodejs-client(仓库名称:googleapis)似乎依赖于nodejs-googleapis-common(仓库名称:googleapis-common)。
"node_modules/googleapis": {
(略)
"dependencies": {
"google-auth-library": "^8.0.2",
"googleapis-common": "^6.0.0"
},
(略)
},
为什么原本就有重试机制,但它为什么不起作用?
为什么之前的nodejs-googleapis-common应该在2019年左右就有了支持,却没有进行重试呢…稍微调查了一下,发现问题出在重试的默认选项上。
在gaxios的README.md中也有提到,当进行调试等操作时,下面的值会被传递。
retryConfig: {
currentRetryAttempt: 0,
retry: 3,
httpMethodsToRetry: [ 'GET', 'HEAD', 'PUT', 'OPTIONS', 'DELETE', [length]: 5 ],
noResponseRetries: 2,
statusCodesToRetry: [
[ 100, 199, [length]: 2 ],
[ 429, 429, [length]: 2 ],
[ 500, 599, [length]: 2 ],
[length]: 3
]
}
我正在使用”创建”(=POST)API来操作Calendar的事件。由于httpMethodsToRetry中没有包含POST请求,所以默认情况下不会进行重试。
此外,由于HTTP的状态码为403,因此“Rate Limit Exceeded”也不在需要重新尝试的状态码列表中。
进行实际尝试 + 成果
通过以下方式,在`httpMethodsToRetry`中添加`POST`,并在`statusCodesToRetry`中添加`403`,确认进行了重试并且没有发生错误。
※ 只摘录相关部分
const { google } = require('googleapis')
google.options({
retryConfig: {
currentRetryAttempt: 0,
retry: 5,
httpMethodsToRetry: ['GET', 'HEAD', 'POST', 'PUT', 'OPTIONS', 'DELETE'],
statusCodesToRetry: [
[100, 199],
[403, 403],
[429, 429],
[500, 599]
],
retryDelay: 500
}
})
另外,关于statusCodesToRetry的指定,虽然[403, 403]可能显得冗余,但是根据以下方式可以用范围进行指定,如果要指定单个状态码,则需要设为[403, 403]。
// If this wasn't in the list of status codes where we want
// to automatically retry, return.
const retryRanges = [
// https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
// 1xx - Retry (Informational, request still processing)
// 2xx - Do not retry (Success)
// 3xx - Do not retry (Redirect)
// 4xx - Do not retry (Client errors)
// 429 - Retry ("Too Many Requests")
// 5xx - Retry (Server errors)
[100, 199],
[429, 429],
[500, 599],
];
config.statusCodesToRetry = config.statusCodesToRetry || retryRanges;
参考:gaxios的源码路径为src/retry.ts。