Java 8 retryWhen with timer显示为破坏合并行为
我在异步消息传递环境(vert.x)中使用RxJava,因此有一个如下所示的流:Java 8 retryWhen with timer显示为破坏合并行为,java-8,rx-java,Java 8,Rx Java,我在异步消息传递环境(vert.x)中使用RxJava,因此有一个如下所示的流: Observable.defer( () -> getEndpoint() ) .mergeWith( getCancellationMessage() ) .flatMap( endpoint -> useEndpoint( endpoint ) ) .retryWhen( obs -> obs.flatMap( error ->
Observable.defer( () -> getEndpoint() )
.mergeWith( getCancellationMessage() )
.flatMap( endpoint -> useEndpoint( endpoint ) )
.retryWhen( obs -> obs.flatMap( error -> {
if ( wasCancelled( error ) ) {
return Observable.error( error );
}
return Observable.timer(/* args */)
}) )
.subscribe( result -> useResult( result ),
error -> handleError( error )
);
getCancellationMessage()
的实现返回一个可观察的流,每当从独立消息源接收到取消消息时,该流就会发出错误。此流只会发出Observable.error()
,并且仅在接收到取消消息时才会发出错误
如果我了解merge
的工作原理,则当getCancellationMessage()
发出错误时,应通过onError
终止整个链
但是,我发现如果在收到取消消息时,retryWhen
操作员正在等待计时器发出,则错误将被忽略,retryWhen
循环将继续,就像从未收到取消一样
我可以通过将Observable.timer()
与相同的getCancellationMessage()
函数合并来修复该行为,但我不明白为什么首先必须这样做
这是merge
/retryWhen
预期的交互吗
编辑:
下面是getCancellationMessage()
函数正在执行的操作的示例:
Observable<T> getCancellationMessage() {
if ( this.messageStream == null ) {
this.messageStream = this.messageConsumer.toObservable()
.flatMap( message -> {
this.messageConsumer.unregister();
if ( isCancelMessage(message) ) {
return Observable.error( new CancelError() );
}
else {
return Observable.error( new FatalError() );
}
});
}
return this.messageStream;
}
可观察的getCancellationMessage(){
if(this.messageStream==null){
this.messageStream=this.messageConsumer.toObservable()
.flatMap(消息->{
this.messageConsumer.unregister();
如果(isCancelMessage(消息)){
返回可观察的.error(new CancelError());
}
否则{
返回可观察的错误(new FatalError());
}
});
}
返回此.messageStream;
}
请注意,我并不拥有this.messageConsumer
的实现——它来自我正在使用的第三方库(vert.x),因此我不控制该可观察对象的实现
据我所知,messageConsumer.toObservable()
方法返回Observable.create()
的结果,并提供一个实例,每当收到新消息时,该实例将调用订阅者的onNext
方法
调用messageConsumer.unregister()
将阻止接收任何进一步的消息
但是,我发现,如果retryWhen操作员在接收到取消消息时等待计时器发出,错误将被忽略,retryWhen循环将继续,就像从未接收到取消一样
操作员retryWhen
将上游Throwable
转换为一个值,并按照您提供的顺序对其进行路由,以获得值响应,从而重试上游或结束流
Observable.error(new IOException())
.retryWhen((Observable<Throwable> error) -> error)
.subscribe();
这里,我们只允许错误通过,如果它不是类型CancellationException
(您可以用错误类型替换它)。这将完成序列
如果您希望序列以错误结束,则需要更改flatMap
逻辑:
.retryWhen(obs -> obs
.flatMap( error -> {
if (error instanceof CancellationException) {
return Observable.error(error);
}
return Observable.timer(/* args */);
})
)
请注意,在flatMap
中返回Observable.empty()
不会结束序列,因为它只是指示要合并的源为空,但可能还有其他内部源。特别是对于retryWhen
,empty()
将无限期挂起序列,因为不会有任何信号指示重试或序列结束
编辑:
根据您的措辞,我假设getCancellationMessage()
是一个热门的观察对象。为了接收它们的事件或错误,必须观察热观测值。当retryWhen
运算符由于timer()
而处于其重试宽限期时,使用getCancellationMessage()
的最顶端的mergeWith
没有订阅任何内容,因此它无法在该点停止计时器
当计时器执行以立即停止时,您必须保留对它的订阅:
Observable<Object> cancel = getCancellationMessage();
Observable.defer( () -> getEndpoint() )
.mergeWith( cancel )
.flatMap( endpoint -> useEndpoint( endpoint ) )
.retryWhen( obs -> obs
.flatMap( error -> {
if (error instanceof CancellationException) {
return Observable.error(error);
}
return Observable.timer(/* args */).takeUntil( cancel );
})
)
.subscribe( result -> useResult( result ),
error -> handleError( error )
);
Observable cancel=getCancellationMessage();
可观察的。延迟(()->getEndpoint())
.合并(取消)
.flatMap(端点->使用端点(端点))
.retryWhen(obs->obs
.flatMap(错误->{
if(取消异常的错误实例){
返回可观测误差(error);
}
返回可观察的.timer(/*args*/).takeUntil(取消);
})
)
.subscribe(结果)->useResult(结果),
错误->处理错误(错误)
);
在这种情况下,如果在计时器执行时触发cancel
,则retryWhen
将立即停止计时器并以取消错误终止
使用takeUntil
是一个选项,正如您所发现的,mergeWith(cancel)
同样有效
但是,我发现,如果retryWhen操作员在接收到取消消息时等待计时器发出,错误将被忽略,retryWhen循环将继续,就像从未接收到取消一样
操作员retryWhen
将上游Throwable
转换为一个值,并按照您提供的顺序对其进行路由,以获得值响应,从而重试上游或结束流
Observable.error(new IOException())
.retryWhen((Observable<Throwable> error) -> error)
.subscribe();
这里,我们只允许错误通过,如果它不是类型CancellationException
(您可以用错误类型替换它)。这将完成序列
如果您希望序列以错误结束,则需要更改flatMap
逻辑:
.retryWhen(obs -> obs
.flatMap( error -> {
if (error instanceof CancellationException) {
return Observable.error(error);
}
return Observable.timer(/* args */);
})
)
请注意,在flatMap
中返回Observable.empty()
不会以