我正在开发一个涉及Hystrix的项目,我决定使用RxJava。现在,忘记Hystrix剩下的了,因为我认为主要的问题是我完全搞砸了正确编写Observable代码。
需要: 我需要一种方法来返回一个代表一些observable的observable,每个observable都运行一个用户任务。我希望Observable能够从任务中返回所有结果,甚至错误。
问题: 可观察的流会因错误而死亡。如果我有三个任务而第二个任务抛出异常,即使它成功,我也从未收到第三个任务。
我的代码:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return Observable
.from(tasks)
.flatMap(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task).toObservable().subscribeOn(this.schedulerFactory.get(groupName));
} catch(Exception ex) {
return Observable.error(ex);
}
});
}
鉴于MyCommand是一个扩展HystrixObservableCommand的类,它返回一个Observable,所以不应该看到我遇到的问题。
尝试1:
如上所述使用Observable.flatMap
尝试2:
使用Observable.concatMapDelayError
代替flatMap
任何帮助都将受到高度赞赏,并且可能导致我因为没有想到自己而感到非常尴尬。
附加代码
此测试使用Observable.flatMap
成功,但在使用Observable.concatMapDelayError
时失败,因为任务不是异步运行的:
java.lang.AssertionError:执行时间超过350毫秒限制:608
@Test
public void shouldRunManagedAsyncTasksConcurrently() throws Exception {
Observable<String> testObserver = executor.observeManagedAsync("asyncThreadPool",getTimedTasks());
TestSubscriber<String> testSubscriber = new TestSubscriber<>();
long startTime = System.currentTimeMillis();
testObserver.doOnError(throwable -> {
System.out.println("error: " + throwable.getMessage());
}).subscribe(testSubscriber);
System.out.println("Test execution time: "+(System.currentTimeMillis()-startTime));
testSubscriber.awaitTerminalEvent();
long execTime = (System.currentTimeMillis()-startTime);
System.out.println("Test execution time: "+execTime);
testSubscriber.assertCompleted();
System.out.println("Errors: "+testSubscriber.getOnErrorEvents());
System.out.println("Results: "+testSubscriber.getOnNextEvents());
testSubscriber.assertNoErrors();
assertTrue("Execution time ran under the 300ms limit: "+execTime,execTime>=300);
assertTrue("Execution time ran over the 350ms limit: "+execTime,execTime<=350);
testSubscriber.assertValueCount(3);
assertThat(testSubscriber.getOnNextEvents(),containsInAnyOrder("hello","wait","world"));
verify(this.mockSchedulerFactory, times(3)).get("asyncThreadPool");
}
上述单元测试的任务:
protected List<EspTask<String>> getTimedTasks() {
EspTask longTask = new EspTask("helloTask") {
@Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(100);
return "hello";
}
};
EspTask longerTask = new EspTask("waitTask") {
@Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(150);
return "wait";
}
};
EspTask longestTask = new EspTask("worldTask") {
@Override
public Object doCall() throws Exception {
Thread.currentThread().sleep(300);
return "world";
}
};
return Arrays.asList(longTask, longerTask, longestTask);
}
答案 0 :(得分:1)
您可以使用Observable.onErrorReturn()
,并返回特殊值(例如null),然后过滤下游的非特殊值。请记住,source observable将在出错时完成。同样取决于用例Observable.onErrorResumeNext()
方法也很有用。如果您对错误通知感兴趣,请使用Observable.materialize()
,这会将商品和onError()
,onComplete()
转换为通知,然后可以通过Notification.getKind()
修改即可。
假设没有try / catch,.toObservable().subscribeOn(this.schedulerFactory.get(groupName));
之后应立即添加上述所有运算符。
答案 1 :(得分:1)
您想使用mergeDelayError
:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return Observable.mergeDelayError(Observable
.from(tasks)
.map(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task).toObservable().subscribeOn(this.schedulerFactory.get(groupName));
} catch(Exception ex) {
return Observable.error(ex);
}
}));
}
请注意,MyCommand构造函数不应抛出任何异常;这样可以更简洁地编写代码:
public <T> Observable<T> observeManagedAsync(String groupName,List<EspTask<T>> tasks) {
return from(tasks)
.map(task -> new MyCommand(task.getTaskId(), groupName, task)
.toObservable()
.subscribeOn(this.schedulerFactory.get(groupName)))
.compose(Observable::mergeDelayError);
}
请注意,这仍然会至少调用一次onError
;如果您需要显式处理所有错误,请使用Either<CommandResult, Throwable>
之类的函数作为返回类型(或处理错误并返回空的observable)。
答案 2 :(得分:1)
使用.materialize()
允许所有排放和错误通过包装通知来处理,然后按照您的意愿处理它们:
.flatMap(task -> {
try {
return new MyCommand(task.getTaskId(),groupName,task)
.toObservable()
.subscribeOn(this.schedulerFactory.get(groupName))
.materialize();
} catch(Exception ex) {
return Observable.error(ex).materialize();
}
});