我正在用RxJava3实现一些多阶段管道样式的处理。我对各个步骤使用了几个连续的.concatMap
调用。副作用是,这些步骤会创建一些(大)临时文件,一旦出现错误和成功,应将其删除。第一步将文件移交给下一个文件。我成功使用Single.using
关闭了文件句柄,但是无法以这种方式删除文件,因为在下一步使用它之前它已经消失了。在大多数情况下,可以在第一步的doOnError
和第二步的using
中删除文件。
但是,在极端情况下,文件“泄漏”即未被删除:如果第一个工作项的第二步失败(引发异常),第二个项完成后 它的第一步(第一个concatMap
的第一步,但尚未开始其第二个步骤(第二个concatMap
),该第二项位于某个中间位置,未被删除,因为它不是当前在任何using
范围内捕获。
我的最小示例是:
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import java.io.File
import io.reactivex.Single
import java.io.FileOutputStream
fun main(args: Array<String>) {
Observable.just(5, 4).subscribeOn(Schedulers.computation())
.concatMapSingle { workItem ->
val file = File("/tmp/foo/work$workItem")
Single.using({ FileOutputStream(file) }, { oStream ->
Single.just(oStream)
.subscribeOn(Schedulers.computation())
.map { os ->
println("Pretending to work on item $workItem")
os.write("File $workItem".toByteArray())
// if (Math.random () > 0.5) throw Exception ("Something else failed")
Thread.sleep(workItem.toLong() * 1000) // Second work item should have a quicker first step than the second step of the first item
Pair(file, workItem) // Propagate both file and item number
}.doOnError { println("Deleting ${file.absolutePath}"); file.delete() }
}, { os -> println("Closing file ${file.absolutePath}"); os.close(); })
}
.concatMapSingle { data1 ->
Single.using({ data1 }, { data2 ->
Single.just(data2)
.subscribeOn(Schedulers.computation())
.map { data ->
val workItem = data.second
println("Continuing pretend work on item ${workItem}");
Thread.sleep(workItem.toLong() * 1000)
// ... More complex stuff here ...
if (workItem == 5) throw Exception("Something failed")
}
}, { data -> println("Deleting ${data.first.absolutePath}"); data.first.delete(); })
}.blockingSubscribe();
}
如果引发异常,则不会删除文件/tmp/foo/work4
,因为工作项“ 4”等待第二个concatMap
处理1。输出为:
Pretending to work on item 5
Closing file /tmp/foo/work5
Continuing pretend work on item 5
Pretending to work on item 4
Closing file /tmp/foo/work4
Deleting /tmp/foo/work5
Exception in thread "main" java.lang.RuntimeException: java.lang.Exception: Something failed
at io.reactivex.internal.util.ExceptionHelper.wrapOrThrow(ExceptionHelper.java:46)
[...]
如果第一个(注释掉)或没有异常抛出,则一切都将被删除。问题与flatMap
相同,但是由于并行运行的事物越来越多,调试起来更加困难。
因此,我的问题是:我可以将某些“清理”功能与该项目超出范围时总是调用的项目(此处为:5、4)相关联吗?