RxJava中的强大错误处理

时间:2017-01-13 20:01:33

标签: java rx-java observable

我正在努力理解在RxJava中为PublishSubject实现强大解决方案所需的正确行为。我已经在各种地方读过onError ...是正确的解决方案,但我没有在哪里放置代码来处理它。请参阅简化示例作为JUnit测试的一部分。突出显示问题的测试是 shouldHandleExceptionsForMultipleSubsSuccessfulOnesContinuing shouldHandleErrorSuccessfulCallForRetryVersionWithSuccessfulOnesContinuing ,这表明在遇到异常后公交的所有处理都会停止。

import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestClientException;

import rx.Observable;
import rx.Subscriber;
import rx.subjects.PublishSubject;
import rx.subjects.SerializedSubject;
import rx.subjects.Subject;

@RunWith(MockitoJUnitRunner.class)
public class TestSubPubRobustness {

    private static final Integer INTEGER_VALUE_OF_14 = Integer.valueOf(14);

    private static final Integer INTEGER_VALUE_OF_12 = Integer.valueOf(12);

    @Mock
    private ValidatoryInterface mockInterface;

    private static final Logger logger = LoggerFactory.getLogger(TestSubPubRobustness.class);

    PublishSubject<Integer> subject;
    Subject<Integer, Integer> inboundMessageBus;

    @Before
    public void setUp() throws Exception {
        subject = PublishSubject.create();
        inboundMessageBus = new SerializedSubject<>(subject);
    }

    @Test
    public void shouldHandleSimplySuccessfulCall() {
        final TestEventHandler eventHandler = new TestEventHandler(mockInterface);
        inboundMessageBus.subscribe(eventHandler);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_12);
        verify(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_12);
        verifyNoMoreInteractions(mockInterface);
    }

    @Test
    public void shouldHandleMultipleSuccessfulCall() {
        final TestEventHandler eventHandler = new TestEventHandler(mockInterface);
        inboundMessageBus.subscribe(eventHandler);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_12);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_14);
        verify(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_12);
        verify(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_14);
        verifyNoMoreInteractions(mockInterface);
    }

    @Test
    public void shouldHandleSimplySuccessfulCallForRetryVersion() {
        final TestEventHandler eventHandler = new TestEventHandler(mockInterface);
        inboundMessageBus.retry().subscribe(eventHandler);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_12);
        verify(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_12);
        verifyNoMoreInteractions(mockInterface);
    }

    @Test
    public void shouldHandleErrorSuccessfulCallForRetryVersion() {
        final TestEventHandler eventHandler = new TestEventHandler(mockInterface);
        inboundMessageBus.retry().subscribe(eventHandler);
        doThrow(new RuntimeException()).when(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_12);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_12);
        verify(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_12);
        verifyNoMoreInteractions(mockInterface);
    }

    @Test
    public void shouldHandleErrorSuccessfulCallForRetryVersionWithSuccessfulOnesContinuing() {
        final TestEventHandler eventHandler = new TestEventHandler(mockInterface);
        inboundMessageBus.onExceptionResumeNext(Observable.empty())
                         .subscribe(eventHandler);
        doThrow(new RestClientException("error")).when(mockInterface)
                                                 .haveBeenCalled(INTEGER_VALUE_OF_12);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_12);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_14);
        verify(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_12);
        verify(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_14);
        verifyNoMoreInteractions(mockInterface);
    }

    @Test
    public void shouldHandleMultipleSubsSuccessfulOnesContinuing() {
        final TestEventHandler eventHandler = new TestEventHandler(mockInterface);
        final TestEventHandler additionalEventHandler = new TestEventHandler(mockInterface);
        inboundMessageBus.retry().subscribe(eventHandler);
        inboundMessageBus.retry().subscribe(additionalEventHandler);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_12);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_14);
        verify(mockInterface, times(2)).haveBeenCalled(INTEGER_VALUE_OF_12);
        verify(mockInterface, times(2)).haveBeenCalled(INTEGER_VALUE_OF_14);
        verifyNoMoreInteractions(mockInterface);
    }

    @Test
    public void shouldHandleExceptionsForMultipleSubsSuccessfulOnesContinuing() {
        final TestEventHandler eventHandler = new TestEventHandler(mockInterface);
        final TestEventHandler additionalEventHandler = new TestEventHandler(mockInterface);
        inboundMessageBus.asObservable()
                         .onErrorReturn(error -> Integer.MAX_VALUE)
                         .retry()
                         .subscribe(eventHandler);
        inboundMessageBus.asObservable()
                         .onErrorReturn(error -> Integer.MAX_VALUE)
                         .retry()
                         .subscribe(additionalEventHandler);
        doThrow(new RuntimeException()).when(mockInterface).haveBeenCalled(INTEGER_VALUE_OF_12);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_12);
        inboundMessageBus.onNext(INTEGER_VALUE_OF_14);
        verify(mockInterface, times(2)).haveBeenCalled(INTEGER_VALUE_OF_12);
        verify(mockInterface, times(2)).haveBeenCalled(INTEGER_VALUE_OF_14);
        verifyNoMoreInteractions(mockInterface);
    }

    private final class TestEventHandler extends Subscriber<Integer> {

        private final ValidatoryInterface validatoryInterface;

        public TestEventHandler(final ValidatoryInterface validatoryInterface) {
            this.validatoryInterface = validatoryInterface;
        }

        @Override
        public void onCompleted() {
            logger.debug("Completed");
        }

        @Override
        public void onError(final Throwable e) {
            logger.error("Argggggggg", e);
        }

        @Override
        public void onNext(final Integer t) {
            logger.debug("Next", t);
            validatoryInterface.haveBeenCalled(t);
        }

    }

    private interface ValidatoryInterface {
        void haveBeenCalled(Integer testNumber);
    }
}

1 个答案:

答案 0 :(得分:1)

AFAIK您无法在错误状态下恢复一次Observable。曾经有一个运算符:onErrorFlatMap。但它在issue 1465中被弃用了。你可以在那里阅读更全面的解释,但基本上逻辑是,一旦Observable处于错误状态,它应该永远不会再发出任何东西。消耗了它。这与只发送一次且仅一次的onError的合同有关。你可以&#34;重启&#34;通过重新连接到源,但&#34;恢复&#34;是被禁止的。

因此,Observable不会像消息总线那样:它会在发出错误后停止传递消息。错误处理操作员永远不会恢复流,而是在错误发生后重新启动它或切换到其他流。

在您的示例中,您说onErrorReturn(e -> MAX_VALUE),一旦源流中发生错误,它将使用仅包含MAX_VALUE的流替换流,如果遇到错误,则有效地以MAX_VALUE结束。然后你说retry(),这意味着如果发生错误,应重新启动流。 AFAIK这两者相互矛盾。

就建议而言,我只建议将您的流视为易失性。在票证中提到了一些像materialize这样的解决方法,但我通常处理它的方法是创建新的流。例如,在http服务器中,我为每个请求创建一个流,而不是一个处理所有请求的流。这种方式将异常与单个请求隔离开来。

我很高兴听到benjchristensen有问题,因为该问题已经写好了。