RxJava:如何从Observable

时间:2016-06-16 17:44:56

标签: java multithreading rx-java reactive-programming observable

我正在开发一个涉及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

  • 好:每个命令都在其自己的线程上进行调度,并且任务以异步方式运行。
  • 错误:在第一个Command异常时,Observable完成了先前成功的结果并发出异常。任何正在进行的命令都会被忽略。

尝试2:

使用Observable.concatMapDelayError代替flatMap

  • 错误:由于某种原因,任务同步运行。为什么??
  • 好:我得到了所有成功的结果。
  • 〜好: OnError获取一个复合异常,其中包含抛出的异常列表。

任何帮助都将受到高度赞赏,并且可能导致我因为没有想到自己而感到非常尴尬。

附加代码

此测试使用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);
}

3 个答案:

答案 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();
            }
        });