如何使用RxJava进行多个API请求并将它们组合起来?

时间:2017-08-01 22:34:18

标签: asynchronous rx-java observable nonblocking rx-java2

我必须进行N REST API调用并合并所有这些调用的结果,否则如果至少有一个调用失败(返回错误或超时),则会失败。 我想使用RxJava,我有一些要求:

  • 在某些情况下,能够配置每个api呼叫的重试次数。我的意思是,如果我有一个重试= 2并且我发出3个请求,每个请求最多需要重试2次,总共最多6个请求。
  • 快失败!如果一个API调用失败了N次(其中N是重试的配置),如果剩余的请求没有结束,它就不会生成,我想返回错误。

如果我希望用一个线程发出所有请求,我需要一个异步Http客户端,不会吗?

感谢。

2 个答案:

答案 0 :(得分:1)

您可以使用Zip运算符在所有请求结束后将所有请求压缩在一起并检查所有请求是否全部成功

 private Scheduler scheduler;
private Scheduler scheduler1;
private Scheduler scheduler2;

/**
 * Since every observable into the zip is created to subscribeOn a different thread, it´s means all of them will run in parallel.
 * By default Rx is not async, only if you explicitly use subscribeOn.
 */
@Test
public void testAsyncZip() {
    scheduler = Schedulers.newThread();
    scheduler1 = Schedulers.newThread();
    scheduler2 = Schedulers.newThread();
    long start = System.currentTimeMillis();
    Observable.zip(obAsyncString(), obAsyncString1(), obAsyncString2(), (s, s2, s3) -> s.concat(s2)
            .concat(s3))
            .subscribe(result -> showResult("Async in:", start, result));
}

private Observable<String> obAsyncString() {
    return Observable.just("Request1")
            .observeOn(scheduler)
            .doOnNext(val -> {
                System.out.println("Thread " + Thread.currentThread()
                        .getName());
            })
            .map(val -> "Hello");
}

private Observable<String> obAsyncString1() {
    return Observable.just("Request2")
            .observeOn(scheduler1)
            .doOnNext(val -> {
                System.out.println("Thread " + Thread.currentThread()
                        .getName());
            })
            .map(val -> " World");
}

private Observable<String> obAsyncString2() {
    return Observable.just("Request3")
            .observeOn(scheduler2)
            .doOnNext(val -> {
                System.out.println("Thread " + Thread.currentThread()
                        .getName());
            })
            .map(val -> "!");
}

在这个例子中,我们只是连结结果,但不是那样做,你可以检查结果并在那里做你的业务逻辑。

您也可以考虑mergecontact

您可以在此处查看更多示例https://github.com/politrons/reactive

答案 1 :(得分:0)

我建议使用 Observable 来包装所有调用。

假设您有调用 API 的函数:

fun restAPIcall(request: Request): Single<HttpResponse>

并且您想调用 n 次。我假设你想用一个值列表调用它们:

val valuesToSend: List<Request>

Observable
    .fromIterable(valuesToSend)
    .flatMapSingle { valueToSend: Request ->
        restAPIcall(valueToSend)
    }
    .toList() // This converts: Observable<Response> -> Single<List<Response>>
    .map { responses: List<Response> -> 
        // Do something with the responses
    }

因此,您可以从列表的元素中调用 restAPI,并将结果作为列表。

另一个问题是重试。你说你想在达到个人上限时重试。这很棘手。我相信 RxJava 对此没有任何开箱即用的功能。

  • 您可以使用 retry(n),总共可以重试 n 次,但是 不是你想要的。
  • 还有一个retryWhen { error -> ... },您可以在其中执行 给出异常的东西,但你会知道什么元素抛出 错误(除非您将元素添加到我认为的异常中)。

我之前没有使用过重试,但它似乎重试了整个 observable,这并不理想。

我的第一种方法是执行以下操作,将每个元素的计数保存在字典或类似内容中,只有在没有单个元素超出限制时才重试。这意味着您必须保留一个计数器并在每次超过任何元素时进行搜索。

val counter = valuesToSend.toMap()

yourObservable
    .map { value: String ->
        counter[value] = counter[value]?.let { it + 1 }?: 0 // Update the counter
        value // Return again the value so you can use it later for the api call
    }
    .map { restAPIcall(it) }
    // Found a way to take yourObservable and readd the element if it doesn't exceeds
    // your limit (maybe in an `onErrorResumeNext` or something). Else throw error