.NET的Google API客户端:如何实现指数补偿

时间:2018-12-02 20:45:00

标签: google-api google-api-dotnet-client

我创建了一种使用.NET Core和Google .NET客户端库将批处理请求中的成员添加到Google组的方法。代码如下:

private void InitializeGSuiteDirectoryService()
{
    _directoryServiceCredential = GoogleCredential
        .FromJson(GlobalSettings.Instance.GSuiteSettings.Credentials)
        .CreateScoped(_scopes)
        .CreateWithUser(GlobalSettings.Instance.GSuiteSettings.User);

    _directoryService = new DirectoryService(new  BaseClientService.Initializer()
    {
        HttpClientInitializer = _directoryServiceCredential,
        ApplicationName = _applicationName
    });
}

public OperationResult<int> AddGroupMembers(Group group, IEnumerable<Member> members)
{
    var result = new OperationResult<int>();
    var memberList = members.ToList();
    var batchRequestCount = 0;

    if (memberList.Any())
    {
        var request = new BatchRequest(_directoryService);

        foreach (var member in memberList)
        {
            batchRequestCount++;
            request.Queue<Members>(_directoryService.Members.Insert(member, group.Id), (content, error, i, message) =>
            {
                if (message.IsSuccessStatusCode)
                {
                    //log OK
                }
                else
                {
                    // Implement Exponential backoff only on the request that failed.
                }
            });
            if (batchRequestCount == 30|| member.Equals(memberList.Last()))
            {
                request.ExecuteAsync().Wait();
                request = new BatchRequest(_directoryService); //Clear queue
            }
        }
    }

    return result;
}

如果成员数量少,则逻辑工作正常;但是,当成员数为100(这是我的Google测试实例中的最大用户数量)时,我从Google收到一条错误消息:“ quotaExceeded”。根据Google的documentation,在其Admin SDK上,批处理请求的限制为1000,当我们达到30的限制时,我将逻辑设置为“执行”。

问题是:每当收到此错误时,如何实施错误处理以重试?他们的文档建议对包含“可重试错误”的响应实施“指数退避”(检查响应时看不到此错误)。

1 个答案:

答案 0 :(得分:0)

因此,这就是我在将成员添加到Gsuite组的呼叫中实现指数退避的最终目的。由于我使用的是dotnet核心,因此我可以使用'Polly',这是一个具有弹性和瞬态故障处理库,可以立即提供此功能。可能需要重构,但是现在代码看起来像这样:

    public OperationResult<int> AddGroupMembers(Group group, IEnumerable<Member> members)
    {
        var result = new OperationResult<int>();
        var memberList = members.ToList();
        var batchRequestCount = 0;

        if (memberList.Any())
        {
            var request = new BatchRequest(_directoryService);

            foreach (var member in memberList)
            {
                retryRequest = false; // This variable needs to be declared at the class level to guarantee the value is available to the original thread running the process.
                batchRequestCount++;
                request.Queue<Members>(_directoryService.Members.Insert(member, group.Id), (content, error, i, message) =>
                {
                    // If error code is 'quotaExceeded' retry the request ( You can add as many error codes as you'd like to retry here) 
                    if (error.Code == 403)
                    {
                        retryRequest = true;
                    }

                });

                // Execute batch request to add members in batches of 30 member max
                if (batchRequestCount == 30|| member.Equals(memberList.Last()))
                {

                    // Below is what the code to retry using polly looks like
                    var response = Policy
                        .HandleResult<HttpResponseMessage>(message => message.StatusCode == HttpStatusCode.Conflict)
                        .WaitAndRetry(new[]
                        {
                            TimeSpan.FromSeconds(1),
                            TimeSpan.FromSeconds(2),
                            TimeSpan.FromSeconds(4)
                        }, (results, timeSpan, retryCount, context) =>
                        {
                            // Log Warn saying a retry was required.
                        })
                        .Execute(() =>
                        {
                            var httpResponseMsg = new HttpResponseMessage();

                            // Execute batch request Synchronously
                            request.ExecuteAsync().Wait();
                            if (retryRequest)
                            {
                                httpResponseMsg.StatusCode = HttpStatusCode.Conflict;
                                retryRequest = false;
                            }
                            else
                            {
                                httpResponseMsg.StatusCode = HttpStatusCode.OK;
                            }

                            return httpResponseMsg;
                        });
                    if (response.IsSuccessStatusCode)
                    {
                       // Log info
                    }
                    else
                    {
                        // Log warn
                    }

                    requestCount = 0;
                    request = new BatchRequest(_directoryService);
                    batchCompletedCount++;
                }
            }
        }

        return result;
    }