我正在使用 https://pkg.go.dev/github.com/sethvargo/go-retry 包从我的应用程序在 GitHub API 服务器上进行重试。该包是其中最简单的一个,有助于重试 Web API 调用。
困扰我的问题是
https://pkg.go.dev/github.com/sethvargo/go-retry#RetryFunc中的
RetryFunc
采用了表单的闭包
type RetryFunc func(ctx context.Context) error
我有很多 GitHub API 围绕着这个重试逻辑,具有不同数量的参数。
下面的示例代码
func retryDecision(resp *github.Response) error {
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != http.StatusNotImplemented) {
return retry.RetryableError(fmt.Errorf("unexpected HTTP status %s", resp.Status))
}
return nil
}
func retryWithExpBackoff(ctx context.Context, f func(ctx context.Context) error) error {
b := retry.NewExponential(1 * time.Second)
b = retry.WithMaxRetries(5, b)
b = retry.WithMaxDuration(5*time.Second, b)
return retry.Do(ctx, b, f)
}
func NewGitHubClient(ctx context.Context, token, repo string) (*github.Client, error) {
r := strings.Split(repo, "/")
client := github.NewClient(nil).WithAuthToken(token)
if err := retryWithExpBackoff(ctx, func(ctx context.Context) error {
_, resp, err := client.Repositories.Get(ctx, r[0], r[1])
if retryErr := retryDecision(resp); retryErr != nil {
return retryErr
}
return err
}); err != nil {
return nil, err
}
return client, nil
}
对于我正在重试实现的每个 API,我最终会进行如下所示的嵌套调用,即这部分代码在我计划实现的每个 API 中重复
if err := retryWithExpBackoff(ctx, func(ctx context.Context) error {
// <-------- API that needs to run by retry --------->
//_, resp, err := client.Repositories.Get(ctx, r[0], r[1])
if retryErr := retryDecision(resp); retryErr != nil {
return retryErr
}
return err
}); err != nil {
return nil, err
}
我如何重写这个,使
retryWithExpBackoff
采用我计划实现的任何API,但采用重试机制?
代码中引用的GitHub API来自https://pkg.go.dev/github.com/google/go-github/v61/github
我设法找到一种方法以这种方式重写重试逻辑。但仍然会欣赏其他方式。
type GitHubAPIRequest func(ctx context.Context) (*github.Response, error)
func retryWithExpBackoff(ctx context.Context, request GitHubAPIRequest) error {
b := retry.NewExponential(1 * time.Second)
b = retry.WithMaxRetries(5, b)
b = retry.WithMaxDuration(5*time.Second, b)
return retry.Do(ctx, b, func(ctx context.Context) error {
response, err := request(ctx)
if retryErr := retryDecision(response); retryErr != nil {
return retryErr
}
return err
})
}
func NewGitHubClient(ctx context.Context, token, repo string) (*github.Client, error) {
r := strings.Split(repo, "/")
client := github.NewClient(nil).WithAuthToken(token)
request := func(ctx context.Context) (*github.Response, error) {
_, resp, err := client.Repositories.Get(ctx, r[0], r[1])
return resp, err
}
if err := retryWithExpBackoff(ctx, request); err != nil {
return nil, err
}
return client, nil
}
虽然这有效,但它将我的 API 限制为
GitHubAPIRequest
的签名,如果我需要访问 GitHub API 的响应而不仅仅是错误,我可能需要稍微调整一下。