尝试使用 RESTFul API 从 Gmail 获取消息,但出现此错误:超出用户速率限制
同时,我实现了节流(1 秒内不再有 5 个调用,当谷歌说 1 秒内不再有 25 个调用时)以及当我遇到任何异常时指数回退,即使所有这些我仍然得到这个例外。
那么,这里可能是什么问题?
谢谢
尝试使用 RESTFul API 从 Gmail 获取消息,但出现此错误:超出用户速率限制
同时,我实现了节流(1 秒内不再有 5 个调用,当谷歌说 1 秒内不再有 25 个调用时)以及当我遇到任何异常时指数回退,即使所有这些我仍然得到这个例外。
那么,这里可能是什么问题?
谢谢
Google API 有一个明确定义的配额,这是 Gmail 的配额:https ://developers.google.com/gmail/api/v1/reference/quota#per-method_quota_usage
这是一个处理配额的小型实用程序类(对于单线程,线程安全实现有点复杂):
public class ApilRateLimiter {
private long timeSliceEnd;
private final int quotaPerSecond;
private int quotaRemaining;
public ApilRateLimiter(final int quotaPerSecond) {
this.quotaPerSecond = quotaPerSecond;
this.quotaRemaining = quotaPerSecond;
this.timeSliceEnd = System.currentTimeMillis() + 1_000L;
}
public void reserve(final int quotaReserved) throws InterruptedException {
if (quotaReserved > quotaPerSecond) {
throw new IllegalArgumentException(
"reservation would never be successful as quota requested is greater than quota per second");
}
final long currentTime = System.currentTimeMillis();
if (currentTime >= timeSliceEnd) {
this.timeSliceEnd = currentTime + 1_000L;
this.quotaRemaining = quotaPerSecond - quotaReserved;
} else if (quotaReserved <= quotaRemaining) {
quotaRemaining -= quotaReserved;
} else {
Thread.sleep(timeSliceEnd - currentTime);
reserve(quotaReserved);
}
}
}
以及 Gmail 配额的定义:
public interface GmailApiLimits {
int QUOTA_PER_SECOND = 250;
int DRAFTS_CREATE = 10;
int DRAFTS_DELETE = 10;
int DRAFTS_GET = 5;
int DRAFTS_LIST = 5;
int DRAFTS_SEND = 100;
int DRAFTS_UPDATE = 15;
int GETPROFILE = 1;
int HISTORY_LIST = 2;
int LABELS_CREATE = 5;
int LABELS_DELETE = 5;
int LABELS_GET = 1;
int LABELS_LIST = 1;
int LABELS_UPDATE = 5;
int MESSAGES_ATTACHMENTS_GET = 5;
int MESSAGES_BATCHDELETE = 50;
int MESSAGES_DELETE = 10;
int MESSAGES_GET = 5;
int MESSAGES_IMPORT = 100;
int MESSAGES_INSERT = 25;
int MESSAGES_LIST = 5;
int MESSAGES_MODIFY = 5;
int MESSAGES_SEND = 100;
int MESSAGES_TRASH = 5;
int MESSAGES_UNTRASH = 5;
int STOP = 50;
int THREADS_DELETE = 20;
int THREADS_GET = 10;
int THREADS_LIST = 10;
int THREADS_MODIFY = 10;
int THREADS_TRASH = 10;
int THREADS_UNTRASH = 10;
int WATCH = 100;
}
你像这样使用它:
this.apiRateLimiter = new ApilRateLimiter(GmailApiLimits.QUOTA_PER_SECOND);
...
apiRateLimiter.reserve(GmailApiLimits.MESSAGES_LIST);
gmailApi.users().messages().list("me")...execute();
...
apiRateLimiter.reserve(GmailApiLimits.MESSAGES_GET);
gmailApi.users().messages().get("me"...execute();
...
基本上,您在调用reserve()
Gmail API 之前调用。如果第二个还有配额,reserve 立即返回,否则它会休眠直到第二个结束。