我想组合两个observable:一个是检查用户是否有权获取数据,另一个是实际获取数据,我想与获取数据并行执行授权以减少总延迟。这是一个不平行的例子:
public static void main(String[] args) {
long startTime = currentTimeMillis();
Observable<Integer> result = isAuthorized().flatMap(isAuthorized -> {
if (isAuthorized) return data();
else throw new RuntimeException("unauthorized");
});
List<Integer> data = result.toList().toBlocking().single();
System.out.println("took: " + (currentTimeMillis() - startTime) + "ms");
System.out.println(data);
assert data.size() == 10;
}
private static Observable<Boolean> isAuthorized() {
return Observable.create( s -> {
try { sleep(5000); } catch (Exception e) {} // simulate latency
s.onNext(true);
s.onCompleted();
});
}
private static Observable<Integer> data() {
return Observable.create(s -> {
for (int i = 0; i < 10; i++) {
try { sleep(1000); } catch (Exception e) {} // simulate long running job
s.onNext(i);
}
s.onCompleted();
});
}
执行此操作的总时间为15秒,如果对授权和数据获取的调用是并行的,则应为10秒。怎么做?理想情况下,我还希望在等待授权完成时,最多有多少数据项被缓存在内存中。
顺便说一句,我读过excellent answer about paralleling observables,但现在还没有解决我的问题。答案 0 :(得分:0)
要使用类型安全执行此操作,我建议为isAuthorized()
发射和data()
发射包装不可变类并合并流,然后减少和过滤以不发出任何内容(未授权)或数据(授权)。
static class AuthorizedData {
final Boolean isAuthorized; //null equals unknown
final Data data; //null equals unknown
AuthOrData(Boolean isAuthorized, Data data) {
this.isAuthorized = isAuthorized;
this.data = data;
}
}
Observable<Data> authorizedData =
isAuthorized()
.map(x -> new AuthorizedData(x, null))
.subscribeOn(Schedulers.io())
.mergeWith(
data().map(x -> new AuthorizedData(null, x))
.subscribeOn(Schedulers.io()))
.takeUntil(a -> a.isAuthorized!=null && !a.isAuthorized)
.reduce(new AuthorizedData(null, null), (a, b) -> {
if (a.isAuthorized!=null && a.data != null)
return a;
else if (b.isAuthorized!=null)
return new AuthorizedData(b.isAuthorized, a.data);
else if (b.data!=null)
return new AuthorizedData(a.isAuthorized, b.data);
else
return a;
})
.filter(a -> a.isAuthorized!=null
&& a.isAuthorized && a.data!=null)
.map(a -> a.data);
如果没有授权,上面的 authorizedData
为空,否则是单个数据项的流。
上述takeUntil
点是在发现用户未获得授权后立即取消订阅data()
。如果data()
可中断(可以关闭套接字或其他),这将非常有用。
答案 1 :(得分:0)
我找到了办法:
Schedulers.io()
订阅两个observable并行运行它们。isAuthorized()
无限次重复repeat()
次发射,因为它通常只发出一个布尔值。 cache()
。以下是解决方案:
public static void main(String[] args) {
long startTime = currentTimeMillis();
Observable<Integer> result = Observable.defer(() -> {
Observable<Boolean> p1 = isAuthorized().cache().repeat().subscribeOn(Schedulers.io());
Observable<Integer> p2 = data().subscribeOn(Schedulers.io());
return Observable.zip(p1, p2, (isAuthorized, item) -> {
if (isAuthorized)
return item;
else
throw new RuntimeException("unauthorized");
});
});
List<Integer> data = result.toList().toBlocking().single();
System.out.println("took: " + (currentTimeMillis() - startTime) + "ms");
System.out.println(data);
assert data.size() == 10;
}
private static Observable<Boolean> isAuthorized() {
return Observable.create( s -> {
try { sleep(5000); } catch (Exception e) {} // simulate latency
s.onNext(true);
s.onCompleted();
});
}
private static Observable<Integer> data() {
return Observable.range(1, 10)
.doOnNext(i -> { try { sleep(1000); } catch (Exception e) {} });
}
从我观察到的这个解决方案中也避免了OutOfMemory错误。两个observable都开始同时发出,但如果授权服务较慢,则将收集数据项,直到填充内部缓冲区。然后RxJava将停止请求数据项,直到授权最终发出布尔值。
当授权以负结果返回时,它也会从数据流中正确取消订阅。