我怎样才能强迫"某种背压以避免rxjava中的多次执行?

时间:2015-06-15 15:27:53

标签: java rx-java backpressure

我有一段代码,他们的工作是更新本地缓存。此缓存更新有两个触发器:

  1. 以固定间隔
  2. 根据要求
  3. 所以这是我如何做到这一点的基本例子。

    forceReloadEvents = new SerializedSubject<Long, Long>(PublishSubject.<Long> create());
    dataUpdates = Observable
        .merge(forceReloadEvents, Observable.timer(0, pullInterval, TimeUnit.SECONDS))
        .flatMap(new Func1<Long, Observable<Boolean>>() {
            @Override
            public Observable<Boolean> call(Long t) {
                return reloadData(); // operation that may take long
            }
        })
        .publish();
    
    dataUpdates.subscribe();
    dataUpdates.connect();
    

    然后我有

    public void forceReload() {
        final CountDownLatch cdl = new CountDownLatch(1);
    
        dataUpdates
            .take(1)
            .subscribe(
                new Action1<Boolean>() {
                    @Override
                    public void call(Boolean b) {
                        cdl.countDown();
                    }
                }
            );
    
        forceReloadEvents.onNext(-1L);
    
        try {
            cdl.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    

    这有效,但问题是当我开始对forceReload()进行多次并发调用时:reloadData()没有并发执行,但元素将排队,进程将循环重新加载数据直到由于之前发布的forceReloadEvents事件导致forceReload()已经完成,所有发送到CountDownLatch的所有事件都已消耗完。

    我想使用onBackPressureDrop,但似乎没有诱导背压,没有任何东西被丢弃。我想要的是强制背压的一些方法,以便合并理解一次只能处理一个元素,并且必须删除任何后续事件,直到当前执行完毕。

    我考虑过使用bufferthrottleFirst,但我不想在每个事件之间强制使用特定时间,而是根据具体情况进行自动缩放。重新加载缓存所需的时间。在throttleFirst完成之前,您可以将其视为reloadData

2 个答案:

答案 0 :(得分:2)

编辑:根据评论,您可以将一个AtomicBoolean作为flatMap中的门,以便在门再次打开之前不会重新加载:

public class AvoidReloadStorm {
    static Observable<Boolean> reload() {
        return Observable.just(true)
        .doOnNext(v -> System.out.println("Reload started..."))
        .delay(10, TimeUnit.SECONDS)
        .doOnNext(v -> System.out.println("Reloaded"));
    }
    public static void main(String[] args) throws Exception {
        Subject<Long, Long> manual = PublishSubject.<Long>create().toSerialized();
        Observable<Long> timer = Observable.timer(0, 5, TimeUnit.SECONDS)
                .doOnNext(v -> System.out.println("Timer reload"));

        AtomicBoolean running = new AtomicBoolean();

        ConnectableObservable<Boolean> src = Observable
        .merge(manual.onBackpressureDrop(), timer.onBackpressureDrop())
        .observeOn(Schedulers.io())
        .flatMap(v -> {
            if (running.compareAndSet(false, true)) {
                return reload().doOnCompleted(() -> {
                    running.set(false);
                });
            }
            System.out.println("Reload rejected");
            return Observable.empty();
        }).publish();

        src.subscribe(System.out::println);

        src.connect();

        Thread.sleep(100000);
    }
}

答案 1 :(得分:2)

感谢akarnokd,我做了这个工作!

这是我根据他的答案创建的解决方案:

Observable<Long> forceReloadEvents = this.forceReloadEvents
    .asObservable()
    .onBackpressureDrop();

Observable<Long> periodicReload = Observable
    .timer(0, pullInterval, TimeUnit.SECONDS)
    .onBackpressureDrop();

final AtomicBoolean running = new AtomicBoolean();

dataUpdates = Observable
    .merge(forceReloadEvents, periodicReload)
    .filter(new Func1<Long, Boolean>() {
        @Override
        public Boolean call(Long t) {
            return running.compareAndSet(false, true);
        }
    })
    .observeOn(Schedulers.io())
    .flatMap(new Func1<Long, Observable<Boolean>>() {
        @Override
        public Observable<Boolean> call(Long t) {
            return reloadData();
        }
    })
    .doOnNext(new Action1<Boolean>() {
        @Override
        public void call(Boolean t) {
            running.set(false);
        }
    })
    .publish();

dataUpdates.subscribe();
dataUpdates.connect();

我不确定onBackpressureDrop在这里是否有用,但我将其设置为预防措施。

forceReload代码不会改变。