我正在尝试组合以不同速度生成的Observable。
考虑从外部服务推送的价格报价流,它们可以(有时)以高速率到达。收到每个报价后,其他服务会根据此价格计算指标并发出这些指标。
我现在想要将这些价格和指标的观察结合起来,并将它们提供给另一项服务。每个价格都有一个时间戳,每个指标都有用于计算的价格的时间戳。价格和指标应按此时间戳分组。
在下面的代码中,我尝试说明这一点。我正在使用Observable.combineLatest(),因为这似乎合适,但正如您在combineLatestToFast()
中看到的那样,如果在计算所有指标之前新价格到货,这种方法将无效。但是,如果在每个指标计算完毕后到达新价格,它就会有效,正如您在combineLatestToFast()
中看到的那样。
好像我错过了什么。我尝试过使用窗口,缓冲和Flowables的不同方法,但似乎我没有理解这一点。
非常感谢任何帮助。谢谢!
package rx;
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.observables.ConnectableObservable;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class RxCombineObservablesReproducer {
public static void main(String[] args) {
RxCombineObservablesReproducer reproducer = new RxCombineObservablesReproducer();
//this will work as indented
reproducer.combineLatestExample();
//this not
reproducer.combineLatestToFast();
}
private void combineLatestExample() {
ConnectableObservable<Data> priceObs = Observable.interval(100, TimeUnit.MILLISECONDS).map(i -> new Data("price", i * 1.00, Instant.now())).publish();
Observable<Data> indicator1Obs = priceObs.map(i -> new Data("indicator1", i.getValue() * 2.00, i.getTs())).delay(10, TimeUnit.MILLISECONDS);
Observable<Data> indicator2Obs = priceObs.map(i -> new Data("indicator2", i.getValue() / 2.0, i.getTs())).delay(50, TimeUnit.MILLISECONDS);
Disposable dis = Observable.combineLatest(priceObs, indicator1Obs, indicator2Obs, (Data price, Data indicator1, Data indicator2) -> {
if (checkSameTs(price, indicator1, indicator2)) {
Map result = new HashMap<String, Data>();
result.put("price", price);
result.put("indicator1", indicator1);
result.put("indicator2", indicator2);
return result;
}
return new HashMap<String, Data>();
}
)
.filter(m -> !m.isEmpty())
.subscribe(System.out::println);
priceObs.connect();
sleep(1000);
dis.dispose();
}
private void combineLatestToFast() {
ConnectableObservable<Data> priceObsToFast = Observable.interval(10, TimeUnit.MILLISECONDS).map(i -> new Data("price", i * 1.00, Instant.now())).publish();
Observable<Data> indicator1ObsToFast = priceObsToFast.map(i -> new Data("indicator1ToFast", i.getValue() * 2.00, i.getTs())).delay(9, TimeUnit.MILLISECONDS);
Observable<Data> indicator2ObsToFast = priceObsToFast.map(i -> new Data("indicator2ToFast", i.getValue() / 2.00, i.getTs())).delay(15, TimeUnit.MILLISECONDS);
System.out.println("\n------------------------------toFast------------------------------\n");
Disposable dis = Observable.combineLatest(priceObsToFast, indicator1ObsToFast, indicator2ObsToFast, (Data priceToFast, Data indicator1ToFast, Data indicator2ToFast) -> {
//System.out.println("combineToFast");
if (checkSameTs(priceToFast, indicator1ToFast, indicator2ToFast)) {
Map result = new HashMap<String, Data>();
result.put("priceToFast", priceToFast);
result.put("indicator1ToFast", indicator1ToFast);
result.put("indicator2ToFast", indicator2ToFast);
return result;
}
return new HashMap<String, Data>();
}
)
.filter(m -> !m.isEmpty())
.subscribe(System.out::println);
priceObsToFast.connect();
sleep(500);
dis.dispose();
System.out.println("The End");
}
private boolean checkSameTs(Data... dataObjects) {
Instant ts = dataObjects[0].getTs();
boolean same = true;
for (Data data : dataObjects) {
if (!data.getTs().equals(ts)) {
same = false;
break;
}
}
return same;
}
private void sleep(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class Data {
private String name;
private Double value;
private Instant ts;
Data(String name, Double value, Instant ts) {
this.name = name;
this.value = value;
this.ts = ts;
}
public String getName() {
return name;
}
Double getValue() {
return value;
}
Instant getTs() {
return ts;
}
@Override
public String toString() {
return "Data{" +
"ts=" + ts +
",name='" + name + '\'' +
", value=" + value +
'}';
}
}
}
输出
{price=Data{ts=2018-06-15T13:06:57.061978200Z,name='price', value=0.0}, indicator1=Data{ts=2018-06-15T13:06:57.061978200Z,name='indicator1', value=0.0}, indicator2=Data{ts=2018-06-15T13:06:57.061978200Z,name='indicator2', value=0.0}}
{price=Data{ts=2018-06-15T13:06:57.156025200Z,name='price', value=1.0}, indicator1=Data{ts=2018-06-15T13:06:57.156025200Z,name='indicator1', value=2.0}, indicator2=Data{ts=2018-06-15T13:06:57.156025200Z,name='indicator2', value=0.5}}
{price=Data{ts=2018-06-15T13:06:57.256024800Z,name='price', value=2.0}, indicator1=Data{ts=2018-06-15T13:06:57.256024800Z,name='indicator1', value=4.0}, indicator2=Data{ts=2018-06-15T13:06:57.256024800Z,name='indicator2', value=1.0}}
{price=Data{ts=2018-06-15T13:06:57.356112300Z,name='price', value=3.0}, indicator1=Data{ts=2018-06-15T13:06:57.356112300Z,name='indicator1', value=6.0}, indicator2=Data{ts=2018-06-15T13:06:57.356112300Z,name='indicator2', value=1.5}}
{price=Data{ts=2018-06-15T13:06:57.456075700Z,name='price', value=4.0}, indicator1=Data{ts=2018-06-15T13:06:57.456075700Z,name='indicator1', value=8.0}, indicator2=Data{ts=2018-06-15T13:06:57.456075700Z,name='indicator2', value=2.0}}
{price=Data{ts=2018-06-15T13:06:57.557074500Z,name='price', value=5.0}, indicator1=Data{ts=2018-06-15T13:06:57.557074500Z,name='indicator1', value=10.0}, indicator2=Data{ts=2018-06-15T13:06:57.557074500Z,name='indicator2', value=2.5}}
{price=Data{ts=2018-06-15T13:06:57.656075600Z,name='price', value=6.0}, indicator1=Data{ts=2018-06-15T13:06:57.656075600Z,name='indicator1', value=12.0}, indicator2=Data{ts=2018-06-15T13:06:57.656075600Z,name='indicator2', value=3.0}}
{price=Data{ts=2018-06-15T13:06:57.755255800Z,name='price', value=7.0}, indicator1=Data{ts=2018-06-15T13:06:57.755255800Z,name='indicator1', value=14.0}, indicator2=Data{ts=2018-06-15T13:06:57.755255800Z,name='indicator2', value=3.5}}
{price=Data{ts=2018-06-15T13:06:57.856452700Z,name='price', value=8.0}, indicator1=Data{ts=2018-06-15T13:06:57.856452700Z,name='indicator1', value=16.0}, indicator2=Data{ts=2018-06-15T13:06:57.856452700Z,name='indicator2', value=4.0}}
------------------------------toFast------------------------------
The End
修改
为了回答第一个答案,我添加了一张大理石图。问题不在于根据价格计算指标。如上所述,这已经由外部服务发生,但是根据密钥(在这种情况下是时间戳)将相应的价格和指标分组在一起,无论它们何时以及以何种顺序到达,然后将它们发送到以下服务。 / p>
请点击链接查看图表,我不允许嵌入图片。
答案 0 :(得分:0)
基于大理石图,由于序列的顺序不同,combineLatest
根本不起作用。您必须为每个流保留哈希映射并向一个公共使用者步骤发出信号,以查看它是否可以从这些哈希映射中建立一行值。
ConcurrentHashMap<Key, A> mapA = new ConcurrentHashMap<>();
ConcurrentHashMap<Key, B> mapB = new ConcurrentHashMap<>();
ConcurrentHashMap<Key, C> mapC = new ConcurrentHashMap<>();
Observable<A> sourceA = ...
Observable<B> sourceB = ...
Observable<C> sourceC = ...
Observable.merge(
sourceA.map(v -> { mapA.put(v.getKey(), v); return v.getKey(); }),
sourceB.map(v -> { mapB.put(v.getKey(), v); return v.getKey(); }),
sourceC.map(v -> { mapC.put(v.getKey(), v); return v.getKey(); }),
)
.flatMap(key -> {
if (mapA.containsKey(key) && mapB.containsKey(key)
&& mapC.containsKey(key)) {
return Observable.just(new Result(
mapA.remove(key), mapB.remove(key), mapC.remove(key)
));
}
return Observable.empty();
})
.subscribe(result -> { /* do something with a row */ });