在documentation for RetryWhen示例中,出现如下情况:
Observable.create((Subscriber<? super String> s) -> {
System.out.println("subscribing");
s.onError(new RuntimeException("always fails"));
}).retryWhen(attempts -> {
return attempts.zipWith(Observable.range(1, 3), (n, i) -> i).flatMap(i -> {
System.out.println("delay retry by " + i + " second(s)");
return Observable.timer(i, TimeUnit.SECONDS);
});
}).toBlocking().forEach(System.out::println);
但如果重试用完,我该如何传播错误?
在 .doOnError(System.out::println)
子句之后添加retryWhen
不会发现错误。它甚至被排出了吗?
在重试之前添加.doOnError(System.out::println)
当显示always fails
所有重试时。
答案 0 :(得分:13)
retryWhen
的文档说它会将onError
通知传递给其订阅者并终止。所以你可以这样做:
final int ATTEMPTS = 3;
Observable.create((Subscriber<? super String> s) -> {
System.out.println("subscribing");
s.onError(new RuntimeException("always fails"));
}).retryWhen(attempts -> attempts
.zipWith(Observable.range(1, ATTEMPTS), (n, i) ->
i < ATTEMPTS ?
Observable.timer(i, SECONDS) :
Observable.error(n))
.flatMap(x -> x))
.toBlocking()
.forEach(System.out::println);
答案 1 :(得分:7)
retryWhen
Javadoc表示:
如果Observable调用onComplete或onError,则重试将调用 子订阅上的onCompleted或onError。
简单地说,如果你想传播异常,一旦你有足够的重试,你就需要重新抛出原始异常。
一种简单的方法是将Observable.range
设置为大于您要重试的次数。
然后在你的zip函数中测试当前的重试次数。如果它等于NUMBER_OF_RETRIES + 1,则返回Observable.error(throwable)
或重新抛出异常。
EG
Observable.create((Subscriber<? super String> s) -> {
System.out.println("subscribing");
s.onError(new RuntimeException("always fails"));
}).retryWhen(attempts -> {
return attempts.zipWith(Observable.range(1, NUMBER_OF_RETRIES + 1), (throwable, attempt) -> {
if (attempt == NUMBER_OF_RETRIES + 1) {
throw Throwables.propagate(throwable);
}
else {
return attempt;
}
}).flatMap(i -> {
System.out.println("delaying retry by " + i + " second(s)");
return Observable.timer(i, TimeUnit.SECONDS);
});
}).toBlocking().forEach(System.out::println);
由于旁边doOnError
不会以任何方式影响Observable - 它只是为您提供了一个钩子,以便在发生错误时执行某些操作。一个常见的例子是记录。
答案 2 :(得分:1)
一种选择是使用Observable.materialize()
将Observable.range()
项转换为通知。然后,一旦发出onCompleted()
,就可以向下游传播错误(Pair
中的示例用于包裹Observable.range()
通知和Observable
的异常
@Test
public void retryWhen() throws Exception {
Observable.create((Subscriber<? super String> s) -> {
System.out.println("subscribing");
s.onError(new RuntimeException("always fails"));
}).retryWhen(attempts -> {
return attempts.zipWith(Observable.range(1, 3).materialize(), Pair::new)
.flatMap(notifAndEx -> {
System.out.println("delay retry by " + notifAndEx + " second(s)");
return notifAndEx.getRight().isOnCompleted()
? Observable.<Integer>error(notifAndEx.getLeft())
: Observable.timer(notifAndEx.getRight().getValue(), TimeUnit.SECONDS);
});
}).toBlocking().forEach(System.out::println);
}
private static class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public L getLeft() {
return left;
}
public R getRight() {
return right;
}
}
答案 3 :(得分:0)
您可以使用Maven Central上的rxjava-extras中的RetryWhen
构建器获取所需的行为。使用最新版本。
Observable.create((Subscriber<? super String> s) -> {
System.out.println("subscribing");
s.onError(new RuntimeException("always fails"));
})
.retryWhen(RetryWhen
.delays(Observable.range(1, 3)
.map(n -> (long) n),
TimeUnit.SECONDS).build())
.doOnError(e -> e.printStackTrace())
.toBlocking().forEach(System.out::println);
答案 4 :(得分:0)
这是我发现在 Kotlin 中实现它的最简单方法。重试 maxRetries
次,如果没有成功尝试,则将错误传递给 onErrorResumeNext
的源。
api.getItems()
.retryWhen { errors ->
errors.zipWith(Observable.range(1, maxRetries + 1), { error, i ->
if (i <= maxRetries) {
Observable.timer(i.toLong(), TimeUnit.SECONDS)
} else {
throw error
}
})
}.onErrorResumeNext { error ->
return@onErrorResumeNext Observable.error(factory.create(error))
}
答案 5 :(得分:-1)
重试后需要使用 onErrorResumeNext
在你的例子中
Observable.create((Subscriber<? super String> s) -> {
System.out.println("subscribing");
s.onError(new RuntimeException("always fails"));
}).retryWhen(attempts -> {
return attempts.zipWith(Observable.range(1, NUMBER_OF_RETRIES + 1), (n, i) -> {
if (i == NUMBER_OF_RETRIES + 1) {
throw Throwables.propagate(n);
}
else {
return i;
}
}).flatMap(i -> {
System.out.println("delay retry by " + i + " second(s)");
return Observable.timer(i, TimeUnit.SECONDS);
});
})
.onErrorResumeNext(t -> {System.out.println("Error after all retries:" + t.getMessage());
return Observable.error(t);
})
.toBlocking().forEach(System.out::println);
在本课程的最底部,您可以看到一个实际的例子来了解它的工作原理。 https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/errors/ObservableExceptions.java
答案 6 :(得分:-1)
你可以使用scan function,它返回一个带有累积索引的对,并决定是否传递错误:
.retryWhen(attempts ->
return .scan(Pair.create(0, null), (index, value) -> Pair.create(index.first + 1, value))
.flatMap(pair -> {
if(pair.first > MAX_RETRY_COUNT) {
throw new RuntimeException(pair.second);
}
return Observable.timer(pair.first, TimeUnit.SECONDS);
});
或者您可以坚持使用zipWith
运算符但增加range
Observable中的数字并返回一对,而不是单独使用索引。这样,您就不会丢失有关之前throwable
的信息。
attempts
.zipWith(Observable.range(1, MAX_RETRY_COUNT + 1), (throwable, i) -> Pair.create(i, throwable))
.flatMap(pair -> {
if(pair.first > MAX_RETRY_COUNT) throw new RuntimeException(pair.second);
System.out.println("delay retry by " + pair.first + " second(s)");
return Observable.timer(pair.first, TimeUnit.SECONDS);
});