处理完所有事件后完成PublishSubject

时间:2017-06-15 07:07:39

标签: multithreading rx-java

我有一个将文件上传到服务器的服务。我将它设置为PublishSubject,我传递定义要上载的文件的事件。 PublishSubject在后​​台线程上顺序处理这些事件(使用单个线程执行程序)。 但是,问题是我需要在上传所有文件后取消订阅PublishSubject。确定是否上传所有文件的方法是PublishSubject在上传最后一个事件时是否收到了另一个事件。如果上载成功完成且未收到任何新事件,则流应终止。 我尝试了以下方法:

publishSubject
    .timeout(file -> uploadFile(file))
    .doOnError(throwable -> if(isTimeout(throwable)) cleanup())
    .subscribe(response -> logResult(response))

如果在PublishSubject observable返回时没有新事件传递给uploadFile,则超时变量将超时,这是预期的行为。除非在uploadFile可观察事件完成之前传递事件时,否则取消订阅Observable

所以我需要的是一种排队事件和按顺序处理每个事件的方法,但如果在处理(在这种情况下上传完成)时队列为空,则流应该终止。怎么能实现呢?

1 个答案:

答案 0 :(得分:0)

可以满足单个成功请求的快乐路径
publishSubject
    .flatMap(file -> uploadFile(file))
    .subscribe(response -> logResult(response),
               throwable -> if(isTimeout(throwable)) cleanup(),
               () -> logResult("completed"));

我认为,uploadFile(file)返回Observable<Response>.flatMap()运算符将接收它收到的请求并将其作为参数传递给uploadFile(file)。当uploadFile()返回的observable完成时,它将首先记录响应,然后记录完整。

错误不需要增强,因为subscribe()中的第二个lambda会处理错误。

我们有两种情况需要担心:1)来自上游的额外请求,以及2)上传期间的超时。在第二种情况下,我们可以在uploadFile()返回的observable上使用运算符,timeout()运算符可以很好地运行。

publishSubject
    .flatMap(file -> uploadFile(file))
    .timeout( 5, SECONDS )
    .subscribe(response -> logResult(response),
               throwable -> if(isTimeout(throwable)) cleanup(),
               () -> logResult("completed"));

在第一种情况下,我们可能不希望一次有多个上传操作。 .flatMap()需要一个额外的运算符,这是支持的最大并发数。您的解决方案现在是:

publishSubject
    .flatMap(file -> uploadFile(file), MAXIMUM_SIMULTANEOUS_UPLOADS)
    .timeout( 5, SECONDS )
    .subscribe(response -> logResult(response),
               throwable -> if(isTimeout(throwable)) cleanup(),
               () -> logResult("completed"));

那么,剩下要做什么了?我相信你原来的解决方案几乎是正确的,但你所缺少的是订阅所在的地方。订阅如下:

  1. publishSubject订阅了请求服务。当请求者没有更多文件要上传时,该订阅将完成。
  2. .flatMap()运算符在其observable内部创建订阅。
  3. .flatMap()运算符将为其创建的每个uploadFile()可观察对象创建订阅。
  4. 最后,.subscribe()步骤是整个链的最终订阅者。
  5. 那么,publishSubject如何从其请求者中取消订阅?您需要在代码之前添加一个步骤以保存订阅,并在您的可观察链中添加一个终止步骤以取消订阅:

    Subscription subscription = requestor.subscribe(publishSubject);
    publishSubject
        .flatMap(file -> uploadFile(file), MAXIMUM_SIMULTANEOUS_UPLOADS)
        .doAfterTerminate( () -> subscription.unsubscribe() )
        .subscribe(response -> logResult(response),
                   throwable -> if(isTimeout(throwable)) cleanup(),
                   () -> logResult("completed"));
    

    修改:已移除timeout()操作符,因为它由uploadFile()处理。