我在异步消息传递环境(vert.x)中使用RxJava,因此有一个如下所示的流程:
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
的工作原理,那么当onError
发出错误时,应通过getCancellationMessage()
终止整个链。
但是,我发现如果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;
}
请注意,我不拥有this.messageConsumer
的实现 - 这来自我正在使用的第三方库(vert.x),所以我不控制该Observable的实现。
根据我的理解,messageConsumer.toObservable()
方法会返回Observable.create()
的结果,该结果随this class实例提供,只要有新消息,它就会调用订阅者的onNext
方法has arrived
对messageConsumer.unregister()
的呼叫会阻止接收任何其他消息。
答案 0 :(得分:2)
但是,我发现如果retryWhen运算符在收到取消消息时等待定时器发出,则忽略该错误并且retryWhen循环继续,就像从未接收到取消一样。
运算符retryWhen
将上游Throwable
转换为值并将其路由到您提供的序列,以获取值响应以重试上游或结束流,因此
Observable.error(new IOException())
.retryWhen((Observable<Throwable> error) -> error)
.subscribe();
将无限期重试,因为内部error
现在被视为值,而不是例外。
retryWhen
本身并不知道哪个error
值应该被认为是不应该重试的值,这是你内心的工作流速:
Observable.defer( () -> getEndpoint() )
.mergeWith( getCancellationMessage() )
.flatMap( endpoint -> useEndpoint( endpoint ) )
.retryWhen( obs -> obs
.takeWhile( error -> !( error instanceof CancellationException ) ) // <-------
.flatMap( error -> Observable.timer(/* args */) )
)
.subscribe( result -> useResult( result ),
error -> handleError( error )
);
在这里,如果错误不是CancellationException
类型,我们只会让错误通过(您可以将其替换为您的错误类型)。这将完成序列。
如果您希望序列以错误结束,我们需要更改flatMap
逻辑:
.retryWhen(obs -> obs
.flatMap( error -> {
if (error instanceof CancellationException) {
return Observable.error(error);
}
return Observable.timer(/* args */);
})
)
请注意,在Observable.empty()
中返回flatMap
并不会结束序列,因为它只是表示要合并的源是空的,但可能还有其他内部源。特别是retryWhen
,empty()
会无限期地挂起序列,因为不会有任何信号表示重试或序列结束。
修改强>
根据你的措辞,我认为getCancellationMessage()
是一个热门的观察者。必须遵守热观察,以便接收他们的事件或错误。当retryWhen
运算符由于timer()
而处于其重试宽限期时,没有任何内容mergeWith
与getCancellationMessage()
订阅,因此无法停止那时的计时器。
您必须在执行timer
时保留对它的订阅才能立即停止:
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 )
);
在这种情况下,如果cancel
在计时器执行时触发,retryWhen
将停止计时器并立即终止取消错误。
使用takeUntil
是一个选项,正如您所发现的那样,mergeWith ( cancel )
也可以正常运作。