处理背压而不丢弃物品或在RxJava中序列化

时间:2016-07-29 06:18:45

标签: java rx-java reactivex backpressure

简而言之,有没有解决方案来解决RxJava中的背压问题而不诉诸丢弃项目,序列化操作或无限制缓冲?

考虑以下任务作为何时有用的示例。

  1. 从磁盘读取数据到内存
  2. 压缩数据
  3. 通过网络传输压缩数据
  4. 直接的方法是在单个后台线程上按顺序执行所有任务,如:

    observeBlocksOfFileContents(file).
        .subscribeOn(backgroundScheduler)
        .map(compressBlock)
        .subscribe(transmitBlock);
    

    虽然这没有问题,但从性能的角度来看,它是次优的,因为运行时是所有三个操作的总和,而不是它们在并行运行时的最大值:

    observeBlocksOfFileContents(file).
        .subscribeOn(diskScheduler)
        .observeOn(cpuScheduler)
        .map(compressBlock)
        .observeOn(networkScheduler)
        .subscribe(transmitBlock);
    

    如果从磁盘读取的数据比压缩和传输的速度快,则可能由于背压而失败。由于以下原因,通常的背压解决方案是不合需要的:

    1. 删除项目:文件必须完整传输而不丢失件
    2. 在单线程上序列化:流水线的性能改进丢失
    3. Callstack阻止:not supported in RxJava
    4. 增加observeOn缓冲区:内存消耗可能是文件大小的几倍
    5. 重新实现observeOn没有MissingBackpressureException:大量的工作和打破流畅的API
    6. 还有其他解决方案吗?或者这是否基本上不适合ReactiveX可观察模型?

1 个答案:

答案 0 :(得分:1)

6)实施observeBlocksOfFileContents,使其支持背压。

文件系统已经是基于拉式的(InputStream.read()会在您想要它时发生而不会被抛出)所以请考虑一个合理的块大小并在每个请求中读取它:

Observable.create(SyncOnSubscribe.createStateful(
    () -> new FileInputStream("file.dat")
    (in, out) -> {
        byte[] buf = new byte[4096];
        int r = in.read(buf);
        if (r < 0) {
            out.onCompleted();
        } else {
            if (r == buf.length) {
                out.onNext(buf);
            } else {
                byte[] buf2 = new byte[r];
                System.arraycopy(buf, 0, buf2, 0, r);
                out.onNext(buf2);
            }
        }

    }, 
    in -> in.close()
));

(为简洁起见省略了试试。)