0

我目前正在使用 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" }

谷歌建议实施指数退避策略来解决这些错误。我已经实现了下面的解决方案,但它似乎不起作用并且对这些错误没有帮助。这是我的实现;

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。 我可以在哪里设置日志语句以了解是否根据指数策略重试请求?

请建议

4

2 回答 2

1

我看到这是一个老问题,但会在这里留下我的答案,以防有人发现它有用。

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

请参阅此处的文档

将刷新令牌请求的 HTTP 请求初始值设定项设置为令牌服务器,或者为无设置 null。

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

像这样的东西应该适用于提供的示例:

    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();
    }
于 2020-10-06T19:40:05.420 回答
0

检查Java 实现的指数退避:

  ExponentialBackOff backoff = new ExponentialBackOff.Builder()
        .setInitialIntervalMillis(500)
        .setMaxElapsedTimeMillis(900000)
        .setMaxIntervalMillis(6000)
        .setMultiplier(1.5)
        .setRandomizationFactor(0.5)
        .build();
    request.setUnsuccessfulResponseHandler(new HttpBackOffUnsuccessfulResponseHandler(backoff));

检查此SO 帖子以获取更多参考。

于 2017-07-02T06:08:17.070 回答