【问题标题】:Gmail API Users.Messages.get throws User Rate Limit ExceededGmail API Users.Messages.get 引发超出用户速率限制
【发布时间】:2014-09-18 22:59:31
【问题描述】:

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

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

那么,这里有什么问题呢?

谢谢

【问题讨论】:

  • 包含出现此错误的代码?
  • 是不是您收到了一些消息,并且在成功检索了一定数量后您开始收到用户速率限制错误?
  • @kroikie - 确实,收到一些消息正常,一段时间后,开始出现速率限制错误。
  • @Stormie - 我的代码是简单的谷歌示例,在速率限制异常的情况下添加了指数退避,并且在检索到 20 条消息后还休眠 1 秒(以免陷入我调用超过25次/秒)。
  • 您运行程序多少次,您可能已经达到free API requests 的上限?我想不出别的了。 :(

标签: java google-api-java-client gmail-api


【解决方案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();
...

基本上,您在调用 Gmail API 之前先调用 reserve()。如果第二个还有配额,reserve 立即返回,否则休眠直到第二个结束。

【讨论】:

  • 你也可以使用 Guava 库中的RateLimiter 实现。它似乎是 google 客户端库的依赖项,因此应该已经在类路径中。
猜你喜欢
  • 2019-04-22
  • 2020-12-13
  • 2021-05-27
  • 1970-01-01
  • 2014-11-12
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 1970-01-01
相关资源
最近更新 更多