Skip to content

IsRetryableError

判断错误是否值得重试。

签名

go
func IsRetryableError(err error) bool

作用

返回 true 的错误:

🎨 一图抵千言

IsRetryableError 的判定逻辑:三类哨兵错误放行重试,其余立即放弃。

⚠️ 429 与 4xx 的关键差异

ErrRateLimited(对应 HTTP 429)虽属 4xx,但被单独标记为可重试——因为限流是暂时的,稍后重试很可能成功。其余 4xx(如 400/401/403)通常是请求本身的永久性问题,重试无意义。

哨兵错误HTTP 状态是否重试原因
ErrRateLimited429✅ 是限流瞬时,稍后恢复
ErrServerError5xx✅ 是服务端临时故障
ErrNotFound404✅ 是资源瞬时缺失
ErrInvalidIP400❌ 否IP 格式永久错误
ErrUnauthorized401❌ 否认证永久失败
ErrForbidden403❌ 否权限永久拒绝

实现

go
func IsRetryableError(err error) bool {
	return errors.Is(err, ErrRateLimited) ||
		errors.Is(err, ErrServerError) ||
		errors.Is(err, ErrNotFound)
}

示例

业务层重试

go
var info *ipapi.IPInfo
for attempt := 0; attempt < 3; attempt++ {
	info, err = client.GetIPInfo(ctx, ip, "json")
	if err == nil {
		break
	}
	if !ipapi.IsRetryableError(err) {
		break // 不可重试,放弃
	}
	time.Sleep(time.Duration(1<<attempt) * time.Second) // 指数退避
}

跳过非重试错误

go
for _, ip := range ips {
	info, err := client.GetIPInfo(ctx, ip, "json")
	if err != nil && !ipapi.IsRetryableError(err) {
		continue // 客户端错误(如 ErrInvalidIP),直接跳过
	}
}

与内置重试的关系

doRequest 已对网络错误和 5xx自动重试 Retries 次。IsRetryableError 用于:

  • 业务层做更复杂的退避策略
  • 区分「该重试」与「该放弃」
  • 限流(ErrRateLimited)的长时间退避
🔍 内置重试 vs 业务层重试,何时用哪个?
  • 内置重试(doRequest:已覆盖网络错误和 5xx,默认 Retries=2(共 3 次请求),间隔 500ms。开箱即用,零配置。
  • 业务层重试:需要更长退避(如限流后等 10s)、跨请求编排(批量 IP 中跳过永久错误)、或自定义退避曲线(指数/抖动)时使用。
  • 二者叠加:内置重试先扛住瞬时抖动,业务层再用 IsRetryableError 做策略性兜底,互不冲突。

⚠️ 4xx 不在内置重试范围

doRequest 只重试网络错误和 5xx,4xx(含 429 限流)不会被内置重试。若需要对 429 做长时间退避,必须在业务层调用 IsRetryableError 自行处理。

下一步

基于 MIT 许可证发布