如何实施Google Gmail API的指数退避?

时间:2017-06-30 17:11:00

标签: java gmail-api exponential-backoff

我目前正在使用Gmail API代表用户发送电子邮件。邮件是逐个发送的,收件人的平均大小是500。 我经常看到{ "code" : 500, "errors" : [ { "domain" : "global", "message" : "Backend Error", "reason" : "backendError" } ], "message" : "Backend Error" }

以及

的一些事件

{ "code" : 429, "errors" : [ { "domain" : "usageLimits", "message" : "Rate Limit Exceeded", "reason" : "rateLimitExceeded" } ], "message" : "Rate Limit Exceeded" }

Google建议实施指数退避策略来解决这些错误。我已经实现了以下解决方案,但它似乎没有工作,并没有帮助解决这些错误。这是我的实现;

public  GoogleCredential createCredentialWithRefreshToken(String accessToken, String refreshToken) 
    {
           GoogleCredential credential =  new GoogleCredential.Builder().setTransport(new NetHttpTransport())
                .setJsonFactory(new JacksonFactory())
                .setClientSecrets(Constants.GOOGLE_CLIENT_ID, Constants.GOOGLE_CLIENT_SECRET)
                .setRequestInitializer(setHttpTimeout())
                .build();
            credential.setAccessToken(accessToken).setRefreshToken(refreshToken);

           return credential;

    }
public HttpRequestInitializer setHttpTimeout() {
          return new HttpRequestInitializer() {

            @Override
            public void initialize(HttpRequest httpRequest) throws IOException {
              httpRequest.setUnsuccessfulResponseHandler(new HttpBackOffUnsuccessfulResponseHandler(backOff()));
              httpRequest.setConnectTimeout(3 * 60000);  // 3 minutes connect timeout
              httpRequest.setReadTimeout(3 * 60000);  // 3 minutes read timeout
            }

            private final ExponentialBackOff.Builder BACK_OFF = new ExponentialBackOff.Builder().setInitialIntervalMillis(500);

            private BackOff backOff() {
               return BACK_OFF.build();
            }   

          };
     }
public static Gmail getGmailServiceForGoogleAccount(GoogleAccount googleAcct){
         Gmail gmailService = null;
        GoogleCredential credential = new Utils().createCredentialWithRefreshToken(googleAcct.getAccess_token(),googleAcct.getRefresh_token());
        gmailService  =   new Gmail.Builder(new NetHttpTransport(),
                                new JacksonFactory(), credential)
                                .setApplicationName("test")
                                .build();

        return gmailService;
     }

此实施有什么问题?我正确地实现了自定义HttpRequestInitializer。 我可以在哪里设置日志语句,以确定是否按照指数政策重试请求?

请建议

2 个答案:

答案 0 :(得分:0)

检查Exponential Backoff是否有Java实现:

examples = [[],
            [2,3,4],
            [1,[2,3,4]],
            [[5,[6],[7,8,9],10,11]],
            [[1,[2,3,4]],[[[5,[6],[7,8,9],10,11]]]],
            [1, [2, [3, 4, [6, 7, 8, 9, 10], [11, 12]], 13]]]

examples.each do |arr|
  a = max_arr(arr)
  puts "\n#{arr}\n  \#=> #{a.size}, #{a}"
end·

[]
  #=> 0, []

[2, 3, 4]
  #=> 3, [2, 3, 4]

[1, [2, 3, 4]]
  #=> 3, [2, 3, 4]

[[5, [6], [7, 8, 9], 10, 11]]
  #=> 5, [5, [6], [7, 8, 9], 10, 11]

[[1, [2, 3, 4]], [[[5, [6], [7, 8, 9], 10, 11]]]]
  #=> 5, [5, [6], [7, 8, 9], 10, 11]

[1, [2, [3, 4, [6, 7, 8, 9, 10], [11, 12]], 13]]
  #=> 5, [6, 7, 8, 9, 10]

选中此SO post以获取更多参考资料。

答案 1 :(得分:0)

我看到这是一个古老的问题,但是如果有人发现它有用,将在此处保留我的答案。

该代码的问题在于它正在.setRequestInitializer()上调用GoogleCredential.Builder,这将设置令牌请求而不是服务API请求的初始化程序。

请参阅文档here

为刷新令牌请求到令牌服务器设置HTTP请求初始化程序,否则为null。

相反,应该在Google服务客户端上配置初始化程序,并且您可以将其与凭据响应处理程序链接在一起,以保留其功能。

对于提供的示例,这样的事情应该起作用:

    public static HttpRequestInitializer requestInitializer(Credential credential) {
        return new HttpRequestInitializer() {

            @Override
            public void initialize(HttpRequest httpRequest) throws IOException {
                httpRequest.setConnectTimeout(3 * 60000);  // 3 minutes connect timeout
                httpRequest.setReadTimeout(3 * 60000);  // 3 minutes read timeout
                // chain response handler with the handler from the credential
                // that handles retries for authentication errors
                HttpUnsuccessfulResponseHandler responseHandler =
                        new HttpBackOffUnsuccessfulResponseHandler(backOff());
                httpRequest.setUnsuccessfulResponseHandler((req, res, retry) ->
                        credential.handleResponse(req, res, retry)
                                || responseHandler.handleResponse(req, res, retry));
            }

            private final ExponentialBackOff.Builder BACK_OFF = new ExponentialBackOff.Builder().setInitialIntervalMillis(500);

            private BackOff backOff() {
                return BACK_OFF.build();
            }

        };
    }
    public static Gmail getGmailServiceForGoogleAccount(GoogleAccount googleAcct){
        GoogleCredential credential = new Utils().createCredentialWithRefreshToken(googleAcct.getAccess_token(),googleAcct.getRefresh_token());
        return new Gmail.Builder(new NetHttpTransport(), new JacksonFactory(), requestInitializer(credential))
                .setApplicationName("test")
                .build();
    }