RxJava - 重试:成功恢复时重置重试计数器

时间:2018-02-19 21:43:09

标签: java rx-java rx-java2

目前我正在使用Observable.retry(n)以便在出错时重试。请看一下这段代码:

@Test
public void testObservableRetry() {
    Observable
            .<String>defer(() -> Observable
                    .just(Notification.createOnNext("valid value"), Notification.createOnError(new Error("Test")))
                    .dematerialize())
            .doOnNext(v -> System.out.println("onNext: " + v))
            .doOnError(e -> System.out.println("onError: " + e.getMessage()))
            .doOnSubscribe(d -> System.out.println("onSubscribe"))
            .doOnDispose(() -> System.out.println("onDispose"))
            .retry(2)
            .take(2) // .take(4) will fail the test
            .blockingLast();
}

上述代码能够在出错后恢复,但重试次数受限于总错误数而非连续错误。

E.g。如果您将.take(2)替换为.take(4) - 测试将失败,因为总错误数将超过4,尽管每次错误后它都会恢复并且能够获得下一个值:

---(v1)----(error)---(v2)----(error)----(v3)-----(error)---(v4)-

我想在恢复成功后找到一种重置计数器的方法。用例例如是网络连接 - 我想在每次断开连接时给出相同数量的尝试,但是在每次成功连接之后我想重置计数器以便允许无限流量,除非每次它能够恢复在不断尝试的次数内。

修改

在此上下文中成功恢复意味着在重试后收到至少1个项目。所以我想只限制连续错误的数量,而不是总错误。

2 个答案:

答案 0 :(得分:2)

这只是一点点修改@ akarnokd的解决方案(只是移除nonEmpty标志)。 @akarnokd - 免费编辑您的解决方案,我将删除此解决方案。

static <T> ObservableTransformer<T, T> retryEmpty(int count) {
    return o -> {
        AtomicInteger remaining = new AtomicInteger(count);
        return o.doOnNext(v -> remaining.lazySet(count))
                .retryWhen(err -> err.flatMap(e ->
                                (remaining.decrementAndGet() == 0)
                                        ? Observable.<T>error(e)
                                        : Observable.just(1)));
    };
}

答案 1 :(得分:1)

您需要retryWhen并在流程之前与其进行通信:

@Test
public void emptyError() {
    AtomicInteger c = new AtomicInteger();
    Observable.fromCallable(() -> { throw new Exception("" + c.incrementAndGet()); })
    .compose(retryEmpty(2))
    .test()
    .assertFailureAndMessage(Exception.class, "2");
}

@Test
public void nonEmptyError() {
    Observable.just(1).concatWith(Observable.error(new Exception()))
    .compose(retryEmpty(2))
    .take(4)
    .test()
    .assertResult(1, 1, 1, 1);
}

static <T> ObservableTransformer<T, T> retryEmpty(int count) {
    return o ->
        Observable.defer(() -> {
            AtomicInteger remaining = new AtomicInteger(count);
            AtomicBoolean nonEmpty = new AtomicBoolean();

            return o.doOnNext(v -> nonEmpty.lazySet(true))
            .retryWhen(err -> 
                err.flatMap(e -> {
                    if (nonEmpty.get()) {
                        nonEmpty.lazySet(false);
                        remaining.lazySet(count);
                    } else
                    if (remaining.decrementAndGet() == 0) {
                        return Observable.error(e);
                    }
                    return Observable.just(1);
                })
             );
        });
}