我有一段代码,他们的工作是更新本地缓存。此缓存更新有两个触发器:
所以这是我如何做到这一点的基本例子。
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
,但似乎没有诱导背压,没有任何东西被丢弃。我想要的是强制背压的一些方法,以便合并理解一次只能处理一个元素,并且必须删除任何后续事件,直到当前执行完毕。
我考虑过使用buffer
或throttleFirst
,但我不想在每个事件之间强制使用特定时间,而是根据具体情况进行自动缩放。重新加载缓存所需的时间。在throttleFirst
完成之前,您可以将其视为reloadData
。
答案 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代码不会改变。