RxJava Backpressure(快速生产者慢消费者)

时间:2015-01-26 13:29:54

标签: java android reactive-programming rx-java

我有执行方法,它在io线程上执行一些耗时的网络调用

例如

/**
 * network call
 * @param value
 * @return
 */
private Observable<Integer> execute(final int value) {
    return Observable.create(new Observable.OnSubscribe<Integer>() {
        @Override
        public void call(Subscriber<? super Integer> subscriber) {

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("done " + value);
            subscriber.onNext(value);
            subscriber.onCompleted();
        }
    }).subscribeOn(Schedulers.io());
}

然后我列出了&#34;命令&#34;必须按顺序执行。 (一个接一个)

示例(Observable.range(x,y)表示命令列表)

public List<Integer> testObservableBackpressure(){
   return Observable.range(0,5).flatMap(new Func1<Integer, Observable<Integer>>() {
        @Override
        public Observable<Integer> call(Integer integer) {
            System.out.println("started " + integer);
            return exeute(integer);
        }
    }).toList().toBlocking().single();
}

以这种方式出局是

started 0
started 1
started 2
started 3
started 4
done 0
done 1
done 2
done 4
done 3

生产速度快于消费

我想要那样的结果

started 0
done 0
started 1
done 1
started 2
done 2
...

,但..

public List<Integer> testObservableBackpressure(){

    return Observable.create(new Observable.OnSubscribe<Integer>() {
        @Override
        public void call(final Subscriber<? super Integer> subscriber) {
            Observable.range(0,5).subscribe(new Subscriber<Integer>() {

                @Override
                public void onStart() {
                    request(1);
                }

                @Override
                public void onCompleted() {
                    subscriber.onCompleted();
                }

                @Override
                public void onError(Throwable e) {
                    subscriber.onError(e);
                }

                @Override
                public void onNext(Integer integer) {
                    System.out.println("started " + integer);
                    execute(integer).subscribe(new Action1<Integer>() {
                        @Override
                        public void call(Integer integer) {
                            subscriber.onNext(integer);
                            request(1);
                        }
                    });
                }
            });
        }
    }).toList().toBlocking().single();
}

这种方式结果如预期

started 0
done 0
started 1
done 1
started 2
done 2
started 3
done 3
started 4

我的问题是,是否有另一种更优雅的方法来处理这个问题?

2 个答案:

答案 0 :(得分:2)

我不确定你在这里需要任何特定的背压策略。只需使用concatMap

如果您使用concatMap而不是flatMap,则每个新输入值仅在Observable发出的最后concatMap完成时才会订阅。在幕后,concatMap使用了SerialSubscription。这应该给你你想要的订单。

答案 1 :(得分:1)

运行代码时得到的输出是:

started 0
started 1
started 2
started 3
started 4
done 1
done 3
done 4
done 2
done 0

请注意,“已完成”消息无序。这是因为您的代码基本上将每次调用的执行并行化execute。对于Observable.range flatMap Observable发出的每个项目,IOScheduler自行运行IOScheduler。因此,每个项目在不同的线程上并行处理,这使得项目无法保持有序并正确交错。实现所需行为的一个选择是确保所有项目都在同一import rx.Observable; import rx.Subscriber; import rx.functions.Func1; import rx.schedulers.Schedulers; import java.util.List; public class Test { private Observable<Integer> execute(final int value) { return Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("done " + value); subscriber.onNext(value); subscriber.onCompleted(); } }); } public List<Integer> testObservableBackpressure(){ return Observable.range(0, 5).flatMap(new Func1<Integer, Observable<Integer>>() { @Override public Observable<Integer> call(Integer integer) { System.out.println("started " + integer); return execute(integer); } }).subscribeOn(Schedulers.io()).toList().toBlocking().single(); } public static void main(String[] args) { new Test().testObservableBackpressure(); } } 上运行(而不是每个项目):

subscribeOn

请注意,唯一的区别是您调用started 0 done 0 started 1 done 1 started 2 done 2 started 3 done 3 started 4 done 4 运算符的位置。此代码产生以下输出:

{{1}}