解决方案代码实现
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.schedulers.Schedulers
import java.util.concurrent.TimeUnit
// 模拟网络请求函数
fun requestA(): Observable<String> {
return Observable.create { emitter ->
// 实际网络请求逻辑,这里模拟成功或失败
if (Math.random() < 0.8) {
emitter.onNext("Result of A")
emitter.onComplete()
} else {
emitter.onError(Throwable("Request A failed"))
}
}
}
fun requestB(param: String): Observable<String> {
return Observable.create { emitter ->
if (Math.random() < 0.8) {
emitter.onNext("Result of B with param: $param")
emitter.onComplete()
} else {
emitter.onError(Throwable("Request B failed"))
}
}
}
fun requestC(param1: String, param2: String): Observable<String> {
return Observable.create { emitter ->
if (Math.random() < 0.8) {
emitter.onNext("Result of C with params: $param1, $param2")
emitter.onComplete()
} else {
emitter.onError(Throwable("Request C failed"))
}
}
}
fun main() {
requestA()
.retryWhen { attempts ->
attempts.zipWith(Observable.range(1, 3)) { throwable, retryCount ->
if (retryCount < 3) {
// 每次重试间隔1秒
Observable.timer(1, TimeUnit.SECONDS)
} else {
Observable.error(throwable)
}
}.flatMap { it }
}
.observeOn(Schedulers.io())
.flatMap { resultA ->
requestB(resultA)
.retryWhen { attempts ->
attempts.zipWith(Observable.range(1, 3)) { throwable, retryCount ->
if (retryCount < 3) {
Observable.timer(1, TimeUnit.SECONDS)
} else {
Observable.error(throwable)
}
}.flatMap { it }
}
}
.flatMap { resultB ->
requestC(resultB, "FixedParamForC")
.retryWhen { attempts ->
attempts.zipWith(Observable.range(1, 3)) { throwable, retryCount ->
if (retryCount < 3) {
Observable.timer(1, TimeUnit.SECONDS)
} else {
Observable.error(throwable)
}
}.flatMap { it }
}
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.single())
.subscribe(
{ resultC -> println("Final result: $resultC") },
{ error -> println("Final error: $error") }
)
}
选择这样实现的原因
- 顺序处理:通过
flatMap
操作符,可以确保请求按照A -> B -> C的顺序依次执行,前一个请求的结果作为下一个请求的参数传递,这符合题目中请求依赖关系的要求。
- 错误处理与重试逻辑:
retryWhen
操作符提供了灵活的重试机制。通过zipWith
和range
操作符,可以实现对每个请求重试3次的逻辑。每次重试前使用timer
操作符设置1秒的间隔,避免频繁重试导致的网络拥塞。
- 线程调度:使用
subscribeOn(Schedulers.io())
将所有网络请求放在IO线程执行,保证主线程的流畅性。observeOn(Schedulers.single())
将最终结果切换到单个线程处理,避免并发问题。
可能存在的性能瓶颈与优化方向
- 性能瓶颈:
- 重试间隔时间固定:重试间隔时间固定为1秒,可能在某些情况下不够灵活。如果网络问题是短暂的,过久的重试间隔会延长整体处理时间;如果网络问题较严重,较短的间隔可能导致过多无效重试。
- 同步阻塞问题:虽然使用了
Schedulers.io()
处理网络请求,但如果某个请求耗时过长,仍然可能阻塞其他请求的重试逻辑。
- 重试次数过多:重试3次可能在某些情况下过多或过少,过多的重试会消耗更多的资源和时间,过少则可能导致一些可恢复的请求失败。
- 优化方向:
- 动态重试间隔:根据失败原因和重试次数动态调整重试间隔,例如指数退避算法,随着重试次数增加,间隔时间成倍增长。
- 异步重试逻辑:可以将重试逻辑进一步优化为异步执行,避免因某个请求的重试阻塞其他请求的处理。
- 智能重试次数:根据业务场景和网络状况,动态调整重试次数,或者引入熔断机制,当失败次数达到一定阈值时,不再重试,直接返回错误。