1

尝试使用 RESTFul API 从 Gmail 获取消息,但出现此错误:超出用户速率限制

同时,我实现了节流(1 秒内不再有 5 个调用,当谷歌说 1 秒内不再有 25 个调用时)以及当我遇到任何异常时指数回退,即使所有这些我仍然得到这个例外。

那么,这里可能是什么问题?

谢谢

4

1 回答 1

1

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 立即返回,否则它会休眠直到第二个结束。

于 2016-05-25T19:06:56.957 回答