我有一个服务器端 NodeJS 应用程序,它使用具有域委托的 Google 服务帐户,通过 Google 日历 API 批处理端点代表其他用户创建 Google 日历事件。
因为我尝试创建超过 600 个事件/分钟(我当前的“每个用户每分钟查询数”限制),所以我想指定其他用户来收取配额,如 使用服务帐户进行正确记帐中所述Google Calendar API 文档中“管理配额”一文的 部分指出:
如果您的应用程序使用域范围委派执行请求,则默认情况下,服务帐户将根据“每个用户每个项目每分钟”配额收费,而不是您模拟的用户。这意味着服务帐户可能会用完配额并受到速率限制,即使它可能在多个用户的日历上运行。您可以通过使用quotaUser URL参数(或x-goog-quota-user HTTP标头)来指示将向哪个用户收费来避免这种情况。这仅用于配额计算。
该文档还链接到 Google Cloud API“限制 API 使用”一文中的限制每个用户的请求,其中指出:
为了防止个人用户用完您的 API 配额,某些 API 包含默认的每用户每分钟限制。如果存在这样的默认限制,您可以按照上一节所述修改该值,以限制每个用户可用的配额。
个人用户由唯一的字符串标识。如果您要创建代表用户发出请求的服务器端应用程序(其中调用代码托管在您拥有的服务器上),则您的请求必须包含quotaUser参数。
要识别用户,请使用quotaUser=userID 参数。该值仅用于短期配额强制执行,因此您不需要使用真实的用户 ID。您可以选择长度不超过 40 个字符的任意字符串来唯一标识用户。
quotaUser 参数仅用于限制每个用户每分钟的请求数。如果您不发送quotaUser参数,则所有调用都将归因于您的服务器计算机,在这种情况下,用户无法限制调用。
但是,无论我如何尝试在请求中包含
quotaUser
,只要我在一分钟内发出超过 600 个请求,我就会开始收到如下所示的 403 错误,这表明 quotaUser
值被忽略:
{
"body": {
"error": {
"code": 403,
"message": "Quota exceeded for quota metric 'Queries' and limit 'Queries per minute per user' of service 'calendar-json.googleapis.com' for consumer 'project_number:xxxxxxxxxxx'.",
"errors": [
{
"message": "Quota exceeded for quota metric 'Queries' and limit 'Queries per minute per user' of service 'calendar-json.googleapis.com' for consumer 'project_number:xxxxxxxxxxx'.",
"domain": "usageLimits",
"reason": "rateLimitExceeded"
}
]
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "RATE_LIMIT_EXCEEDED",
"domain": "googleapis.com",
"metadata": {
"service": "calendar-json.googleapis.com",
"quota_limit": "defaultPerMinutePerUser",
"quota_metric": "calendar-json.googleapis.com/default",
"consumer": "projects/xxxxxxxxxxx"
}
}
]
}
}
无法找到如何包含quotaUser参数的具体示例,我尝试了所有可以想到的组合,但都无济于事,包括(并确保为每个请求改变传递给quotaUser的实际值):
在请求部分url的查询字符串中:
POST https://www.googleapis.com/batch/calendar/v3 HTTP/1.1
Authorization: /*Auth token*/
Content-Type: multipart/mixed; boundary=batch_foobarbaz
--batch_foobarbaz
Content-Type: application/http
Content-ID: <xxx>
POST /calendar/v3/calendars/primary/events?quotaUser=usernamemydomaincom
Content-Type: application/json
{{ body }}
--batch_foobarbaz--
在请求部分:
POST https://www.googleapis.com/batch/calendar/v3 HTTP/1.1
Authorization: /*Auth token*/
Content-Type: multipart/mixed; boundary=batch_foobarbaz
--batch_foobarbaz
Content-Type: application/http
Content-ID: <xxx>
POST /calendar/v3/calendars/primary/events
Content-Type: application/json
quotaUser: usernamemydomaincom
{{ body }}
--batch_foobarbaz--
在请求部分使用标头名称格式:
POST https://www.googleapis.com/batch/calendar/v3 HTTP/1.1
Authorization: /*Auth token*/
Content-Type: multipart/mixed; boundary=batch_foobarbaz
--batch_foobarbaz
Content-Type: application/http
Content-ID: <xxx>
POST /calendar/v3/calendars/primary/events
Content-Type: application/json
X-Goog-Quota-User: usernamemydomaincom
{{ body }}
--batch_foobarbaz--
以及对批处理端点本身的请求
POST https://www.googleapis.com/batch/calendar/v3?quotaUser=usernamemydomaincom HTTP/1.1
Authorization: /*Auth token*/
Content-Type: multipart/mixed; boundary=batch_foobarbaz
--batch_foobarbaz
Content-Type: application/http
Content-ID: <xxx>
POST /calendar/v3/calendars/primary/events
Content-Type: application/json
{{ body }}
--batch_foobarbaz--
和
POST https://www.googleapis.com/batch/calendar/v3 HTTP/1.1
Authorization: /*Auth token*/
Content-Type: multipart/mixed; boundary=batch_foobarbaz
X-Goog-Quota-User: usernamemydomaincom
--batch_foobarbaz
Content-Type: application/http
Content-ID: <xxx>
POST /calendar/v3/calendars/primary/events
Content-Type: application/json
{{ body }}
--batch_foobarbaz--
如何正确提供
quotaUser
参数,以便不受每个用户每分钟 600 个请求的限制?
如果您正在寻找示例,您可以查看 Node JS 库文档中的 全局选项:
const {google} = require('googleapis');
google.options({
// All requests from all services will contain the above query parameter
// unless overridden either in a service client or in individual API calls.
params: {
quotaUser: '[email protected]'
}
});
但是,当您获得
RATE_LIMIT_EXCEEDED
时,另一种解决方案是按照文档中的建议实施指数退避。
我也在寻找同样的答案。您是否尝试过使用 principal 作为值而不是电子邮件地址?我怀疑 40 个字符的限制暗示这正是他们所期望的。也许?
我们会尝试,如果发现的话我们会在这里更新!
当传递主要用户时,quotaUser 将被忽略。看 quotaUser 是如何工作的? 为了超过 600 个用户配额限制,您可能必须在对日历进行 api 调用之前模拟不同的用户。
const newAuths = userIds.map(
(userId) =>
new google.auth.JWT({
email: authProp.email, //service account
key: authProp.key,
scopes: SCOPES,
subject: userId, //<===This helped
})
);
// Now you can make 600 call with newAuths[0]
// and next 600 with newAuths[1] and so on,
// till you reach 10,000 upper limit set for you app.