cenkalti/backoff
GitHub: cenkalti/backoff
一个 Go 语言的指数退避重试库,为失败操作提供指数递增间隔的自动重试能力。
Stars: 4038 | Forks: 218
# 指数退避 [][godoc]
这是从 [Google 的 Java HTTP 客户端库][google-http-java-client] 移植到 Go 的指数退避算法。
[指数退避][exponential backoff wiki]
是一种算法,它使用反馈来成倍地降低某个过程的速率,
以便逐渐找到可接受的速率。
重试次数会呈指数级增加,并在达到某个阈值时停止增加。
## 安装
```
go get github.com/cenkalti/backoff/v7
```
请注意导入路径末尾的 `/v7`。
## 用法
在大多数情况下,将你想要重试的操作包装在 `Retry` 中:
```
result, err := backoff.Retry(ctx, func() (string, error) {
resp, err := http.Get("https://www.example.com")
if err != nil {
return "", err // transient: Retry will try again
}
defer resp.Body.Close()
switch {
case resp.StatusCode >= 500:
return "", fmt.Errorf("server error: %s", resp.Status) // retried
case resp.StatusCode >= 400:
// client errors won't fix themselves, so stop retrying.
return "", backoff.Permanent(fmt.Errorf("client error: %s", resp.Status))
}
return "ok", nil
}, backoff.WithMaxTries(5))
```
`Retry` 至少运行一次操作,并使用指数
退避不断重试,直到成功、返回 `Permanent` 错误或达到限制。
有关更完整的示例,请参阅 [example_test.go][example],有关可用选项(`WithBackOff`、`WithMaxTries`、`WithMaxElapsedTime`、
`WithNotify`),请参阅[包文档][godoc]。
如果 `Retry` 不符合你的需求,可以从 [retry.go][retry-src] 中复制它并进行修改。
### 处理错误
失败时,`Retry` 总是返回一个 `*RetryError`。它携带了最后一次操作的错误(`LastErr`)以及停止重试的原因(`Cause`)。使用 `errors.Is` 检查它,或者使用 `AsRetryError` 获取该结构体:
```
result, err := backoff.Retry(ctx, operation)
switch {
case errors.Is(err, backoff.ErrPermanent):
// the operation returned a Permanent error
case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
// the caller's context was cancelled or its deadline expired
case errors.Is(err, backoff.ErrMaxElapsedTime):
// the WithMaxElapsedTime budget was exhausted
case errors.Is(err, backoff.ErrExhausted):
// WithMaxTries was reached or the backoff policy returned Stop
}
// The last operation error is always available, whatever the cause:
if re := backoff.AsRetryError(err); re != nil {
log.Printf("gave up after last error: %v", re.LastErr)
}
```
使用 `backoff.Permanent(err)` 将错误标记为不可重试;`Retry` 会立即停止并返回一个 `*RetryError`,其 `Cause` 为 `ErrPermanent`,`LastErr` 为 `err`。
### 限制总时间
两个独立的限制控制着 `Retry` 的运行时长,并且它们的行为有所不同:
- **context deadline** (`context.WithTimeout`) 是响应式的:它会中断尝试之间的等待,并且——如果你的操作观察了 context——可以中止正在进行的尝试。`Retry` 将其报告为 `context.DeadlineExceeded`。
- **`WithMaxElapsedTime`** 仅限制重试调度:它在尝试之间进行检查,从不中断正在运行的操作,并被报告为 `ErrMaxElapsedTime`。
`WithMaxElapsedTime` 默认为 15 分钟,因此**除非你覆盖它,否则这两个限制都会生效** —— 传入 `backoff.WithMaxElapsedTime(0)` 以完全依赖 context。
## 贡献
* 我想让这个库尽可能小。
* 请不要在没有开启 issue 并首先讨论的情况下发送 PR。
* 如果提议的更改不是一个常见的用例,我可能不会接受它。
标签:EVTX分析, Go, Ruby工具, 容错处理, 开发库, 指数退避, 日志审计, 网络请求, 重试机制