如何为Retrofit的RxJava Single请求添加重试延迟/指数退避?
实现带延迟/指数退避的RxJava Single重试机制
嗨,这个需求其实RxJava已经帮我们准备好了现成的方案,不用自己手动实现延迟逻辑~你现在用的retry()方法虽然能指定重试次数和断言,但没法控制重试间隔,这时候换成retryWhen()就能完美解决问题,它允许我们自定义重试的时机和延迟策略。
核心思路
retryWhen()接收一个Flowable<Throwable>作为输入,我们可以在这个流里:
- 保留你原来的错误断言逻辑,只对符合条件的错误进行重试;
- 绑定重试次数,控制最多重试3次;
- 加入延迟或指数退避逻辑,让重试间隔逐渐拉长。
方案1:固定延迟重试
如果希望每次重试都间隔固定时间(比如1秒),可以用下面的代码替换你原来的applySchIoAndRetry()方法:
import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.core.SingleTransformer; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.exceptions.Exceptions; import retrofit2.HttpException; import java.util.concurrent.TimeUnit; public <T> SingleTransformer<T, T> applySchIoAndRetry() { return upstream -> upstream .subscribeOn(Schedulers.io()) .retryWhen(throwableFlowable -> throwableFlowable // 把错误流和重试次数(1-3)绑定,控制最多重试3次 .zipWith(Flowable.range(1, 3), (throwable, retryCount) -> { // 保留你原来的断言逻辑:仅非400的HttpException才重试 if (throwable instanceof HttpException) { HttpException exception = (HttpException) throwable; if (exception.code() != 400) { return retryCount; } } // 不符合重试条件,直接抛出错误终止流程 throw Exceptions.propagate(throwable); }) // 每次重试前延迟1秒 .flatMap(retryCount -> Flowable.timer(1, TimeUnit.SECONDS)) ); }
方案2:指数退避重试
如果想优化重试策略,比如第一次延迟1秒,第二次2秒,第三次4秒(指数增长),避免短时间内频繁请求给服务器造成压力,只需要修改延迟计算逻辑即可:
public <T> SingleTransformer<T, T> applySchIoAndRetryWithExponentialBackoff() { return upstream -> upstream .subscribeOn(Schedulers.io()) .retryWhen(throwableFlowable -> throwableFlowable .zipWith(Flowable.range(1, 3), (throwable, retryCount) -> { if (throwable instanceof HttpException) { HttpException exception = (HttpException) throwable; if (exception.code() != 400) { return retryCount; } } throw Exceptions.propagate(throwable); }) // 指数退避:第n次重试延迟 2^(n-1) 秒 .flatMap(retryCount -> { long delaySeconds = (long) Math.pow(2, retryCount - 1); return Flowable.timer(delaySeconds, TimeUnit.SECONDS); }) ); }
关键细节说明
- 重试次数控制:
Flowable.range(1,3)表示最多重试3次,和你原来的retry(3)逻辑一致(原始请求+3次重试,共4次请求); - 错误断言保留:在
zipWith的lambda里先判断错误是否符合重试条件,不符合就直接抛出异常终止重试; - 延迟实现:用
Flowable.timer()生成延迟信号,当这个信号发射时,RxJava就会触发重试; - 异常处理:用
Exceptions.propagate()包装checked exception,避免lambda里的编译错误。
你的调用代码完全不需要修改,还是像原来一样用compose()应用这个Transformer就行:
public Single<Foo> bar() { return apiService.foobar() .compose(rxHelper.applySchIoAndRetry()); }
内容的提问来源于stack exchange,提问作者Rajath
相关产品推荐
相关产品推荐

