在服务器维护过程中,经常遇到外部接口不稳定、ref="/tag/72/" style="color:#E3A3CF;font-weight:bold;">网络抖动导致的请求失败。比如某个定时任务需要调用第三方API获取数据,但偶尔因为对方服务短暂不可用而中断。这时候直接抛异常显然不够友好,更合理的做法是加个重试机制。
协程中的retry怎么用
Kotlin协程提供了 retry 相关的扩展能力,虽然标准库没有直接叫 retry() 的函数,但我们可以通过 supervisorScope 配合循环和延迟来实现可靠的重试逻辑。
比如写一个通用的重试方法,最多尝试三次,每次间隔500毫秒:
suspend fun <T> retry(maxRetries: Int, block: suspend () -> T): T {
var failure: Throwable? = null
repeat(maxRetries + 1) {
try {
return block()
} catch (e: Throwable) {
failure = e
delay(500L)
}
}
throw failure!!
}
然后在实际调用时这样使用:
val result = retry(3) {
httpClient.get<String>("https://api.example.com/status")
}
结合指数退避更稳妥
如果每次都等同样时间,可能刚好撞上对方服务高峰期。可以改成指数退避策略,第一次等500毫秒,第二次1秒,第三次2秒:
var attempts = 0
repeat(maxRetries + 1) {
try {
return block()
} catch (e: Throwable) {
if (++attempts >= maxRetries) throw e
delay((500L shl attempts).coerceAtMost(10000)) // 最大不超过10秒
}
}
这种写法在面对突发性负载或短暂熔断时特别有用,避免雪崩效应。
注意别把错误请求一直重试
有些错误重试也没意义,比如401未授权或者404找不到资源。可以在捕获异常后判断类型,只对网络超时、连接失败这类临时问题重试:
catch (e: IOException) {
// 网络层问题,值得重试
} catch (e: HttpException) {
if (e.response().code() in setOf(500, 502, 503)) {
// 服务端内部错误,可以重试
} else {
throw e // 其他HTTP错误直接抛出
}
}
这样既保证了容错性,又不会无意义地浪费资源。
线上系统难免遇到各种波动,加上一层智能重试,能让服务稳定不少。特别是在做微服务间通信时,这点小改进往往能减少一大半告警。