Java 8 retryWhen with timer显示为破坏合并行为

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 ->

我在异步消息传递环境(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
的工作原理,则当
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()
不会以