我有一个将文件上传到服务器的服务。我将它设置为PublishSubject
,我传递定义要上载的文件的事件。 PublishSubject
在后台线程上顺序处理这些事件(使用单个线程执行程序)。
但是,问题是我需要在上传所有文件后取消订阅PublishSubject
。确定是否上传所有文件的方法是PublishSubject
在上传最后一个事件时是否收到了另一个事件。如果上载成功完成且未收到任何新事件,则流应终止。
我尝试了以下方法:
publishSubject
.timeout(file -> uploadFile(file))
.doOnError(throwable -> if(isTimeout(throwable)) cleanup())
.subscribe(response -> logResult(response))
如果在PublishSubject
observable返回时没有新事件传递给uploadFile
,则超时变量将超时,这是预期的行为。除非在uploadFile
可观察事件完成之前传递事件时,否则取消订阅Observable
。
所以我需要的是一种排队事件和按顺序处理每个事件的方法,但如果在处理(在这种情况下上传完成)时队列为空,则流应该终止。怎么能实现呢?
答案 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"));
那么,剩下要做什么了?我相信你原来的解决方案几乎是正确的,但你所缺少的是订阅所在的地方。订阅如下:
publishSubject
订阅了请求服务。当请求者没有更多文件要上传时,该订阅将完成。.flatMap()
运算符在其observable内部创建订阅。.flatMap()
运算符将为其创建的每个uploadFile()
可观察对象创建订阅。.subscribe()
步骤是整个链的最终订阅者。那么,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()
处理。