rxjava将2个调用与错误处理结合在一起,以延迟方式失败

rxjava将2个调用与错误处理结合在一起,以延迟方式失败,java,rx-java,rx-java3,Java,Rx Java,Rx Java3,用例是, 数据有两个来源: 服务1-从源1获取 服务2-从源2获取 应用程序应至少从source-1返回数据。如果source-2一切正常-数据将“增强”,比如乘以100 服务1调用服务2 如果所有成功的用户都从服务1和服务2获取数据 如果服务2上存在错误,则用户仅从服务1获取数据(至少) 如果服务1上有错误-用户将收到错误 有一个hello world bench代码,模拟此场景: import io.reactivex.rxjava3.core.Observable; import java

用例是, 数据有两个来源:

  • 服务1-从源1获取
  • 服务2-从源2获取
  • 应用程序应至少从source-1返回数据。如果source-2一切正常-数据将“增强”,比如乘以100

    服务1调用服务2

    如果所有成功的用户都从服务1和服务2获取数据 如果服务2上存在错误,则用户仅从服务1获取数据(至少) 如果服务1上有错误-用户将收到错误

    有一个hello world bench代码,模拟此场景:

    import io.reactivex.rxjava3.core.Observable;
    import java.util.concurrent.TimeUnit;
    
    class Response {
    
        public Integer value;
        public String warning;
        public Response(Integer value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "Response{" +
                    "value=" + value +
                    ", warning='" + warning + '\'' +
    
                    '}';
        }
    }
    
    class Service1 {
    
        public Observable<Response> call(int arg) {
            return Observable
                    .just(
                            new Response(1),
                            new Response(2),
                            new Response(3),
                            new Response(4))
                    .delay(100, TimeUnit.MILLISECONDS);
        }
    }
    
    class Service2 {
    
        public Observable<Response> call(int arg) {
    
            if ( arg % 2 == 0) {
    
                System.out.println("service 2: " + arg);
    
                return Observable
                        .just(new Response(100 * arg)) // service 2 multiplies x 100 on the result it gets from the service 1 
                        .delay(10, TimeUnit.MILLISECONDS);
    
            } else {
    
                System.out.println("service 2: " + arg);
    
                return Observable.error(new RuntimeException("service 2 error"));
            }
        }
    }
    
    public class Step1 {
    
        static Service1 service1 = new Service1();
        static Service2 service2 = new Service2();
    
        public static void main(String[] args) throws InterruptedException {
    
            var oo1 = service1.call(1);
    
            var oo3 = oo1.switchMapDelayError(x -> {
    
                final Observable<Response> oo2 = service2.call(x.value);
    
                return oo2
                        .onErrorReturn((ex) -> {
                            //System.out.println("Error handling..." + ex.getMessage() + " " + x);
                            x.warning = ex.getMessage();
                            return x; // returns at least service1 result
                        });
            });
    
            oo3.subscribe(x -> {
                System.out.println(x);
            });
    
    
            Thread.sleep(100000);
        }
    
    }
    
    问题是:没有预期值:
    value=200
    2*100

    但是,如果我在service2.call()//.delay(10,TimeUnit.millides)处注释延迟,则它将得到预期的结果:

    service 2: 1
    Response{value=1, warning='service 2 error'}
    service 2: 2
    Response{value=200, warning='null'}
    service 2: 3
    Response{value=3, warning='service 2 error'}
    service 2: 4
    Response{value=400, warning='null'}
    
    问题是:为什么在service2.call()上使用
    .delay(10,TimeUnit.millises)时无法生成值=200?这个解决方案有什么问题,我错过了什么


    谢谢。

    您的问题是
    switchMapDelayError
    操作员。您应该使用concatMap或flatMap

    我冒昧地为您的用例编写了一个测试。请注意,始终使用重载来提供
    计划程序
    ,以便为测试提供
    测试计划程序

    switchMap做什么? 在每个上游发射开关上,映射订阅给定的内部流。当一个新值从上游发出时,旧的内部流将被取消订阅,并再次调用switchMap的lambda以订阅新的内部流

    问题可能在于以下代码:

    return Observable
                .just(
                        new Response(1),
                        new Response(2),
                        new Response(3),
                        new Response(4))
                .delay(100, TimeUnit.MILLISECONDS);
    
    它几乎立即一个接一个地在堆栈上发出响应1到4,并且每个发出在另一个线程上延迟。因此,响应1到4几乎会立即发出。它们不会像100毫秒时的响应(1)和200毫秒时的响应(2)那样发出

    让我们看看输出是为了什么

    Observable.just(
        new Response(1), //
        new Response(2),
        new Response(3),
        new Response(4))
        .delay(100, TimeUnit.MILLISECONDS)
        .subscribe(r -> {
          System.out.println("received value at " + Schedulers.io().now(TimeUnit.MILLISECONDS));
        });
    
    输出

    received value at 1607432032768
    received value at 1607432032769
    received value at 1607432032769
    received value at 1607432032769
    
    因此,所有值几乎立即发出,并用switchMap相互覆盖。以前发出的值几乎立即被新值抵消

    解决方案 使用concatMap或flatMap或更改测试设置以100ms的间隔发出每个值

    flatMap只订阅每个值,默认情况下最大为128个内部流。当内部流完成时,ConcatMap将只订阅下一个值

    试验

    公共类So65193002{
    @试验
    作废so(){
    TestScheduler TestScheduler=新的TestScheduler();
    Service1 Service1=新Service1(testScheduler);
    Service2 Service2=新的Service2(testScheduler);
    可观察服务1呼叫=服务1呼叫(1);
    可观测组合=
    service1Call.concatmapperDelayError(
    x->{
    返回服务2
    .调用(x.value)
    A.报税表(
    (ex)->{
    x、 警告=例如getMessage();
    return x;//至少返回service1结果
    });
    },
    正确的);
    TestObserver test=组合的.test();
    testScheduler.advanceTimeBy(1,时间单位为小时);
    测试.资产价值计数(4)
    .资产价值(
    0,
    r->{
    资产(r.价值)。isEqualTo(1);
    断言(r.warning).isNotEmpty();
    返回true;
    })
    .资产价值(
    1.
    r->{
    资产(r.价值)。isEqualTo(200);
    assertThat(r.warning).isNull();
    返回true;
    })
    .资产价值(
    3.
    r->{
    资产(r.价值)。isEqualTo(400);
    assertThat(r.warning).isNull();
    返回true;
    });
    }
    }
    
    领域

    class Response {
      public Integer value;
      public String warning;
    
      public Response(Integer value) {
        this.value = value;
      }
    
      @Override
      public String toString() {
        return "Response{" + "value=" + value + ", warning='" + warning + '\'' + '}';
      }
    }
    
    class Service1 {
      private final Scheduler scheduler;
    
      Service1(Scheduler scheduler) {
        this.scheduler = scheduler;
      }
    
      public Observable<Response> call(int arg) {
        return Observable.just(
                new Response(1), //
                new Response(2),
                new Response(3),
                new Response(4))
            .delay(100, TimeUnit.MILLISECONDS, scheduler);
      }
    }
    
    class Service2 {
      private final Scheduler scheduler;
    
      Service2(Scheduler scheduler) {
        this.scheduler = scheduler;
      }
    
      public Observable<Response> call(int arg) {
        if (arg % 2 == 0) {
          return Observable.just(new Response(100 * arg)).delay(10, TimeUnit.MILLISECONDS, scheduler);
    
        } else {
          return Observable.error(new RuntimeException("service 2 error"));
        }
      }
    }
    
    类响应{
    公共整数值;
    公共字符串警告;
    公共响应(整数值){
    这个值=值;
    }
    @凌驾
    公共字符串toString(){
    返回“Response{“+”value=“+value+”,warning=''+warning+'\''+'}”;
    }
    }
    类别服务1{
    专用最终调度器;
    服务1(调度程序){
    this.scheduler=调度程序;
    }
    公共可观察呼叫(int arg){
    返回可观察的(
    新的答复(1)//
    新的答复(2),
    新的答复(3),
    新的答复(4))
    .延迟(100,TimeUnit.ms,调度程序);
    }
    }
    类别服务2{
    专用最终调度器;
    服务2(调度程序){
    this.scheduler=调度程序;
    }
    公共可观察呼叫(int arg){
    如果(参数%2==0){
    返回可观察的.just(新响应(100*arg)).delay(10,TimeUnit.ms,调度程序);
    }否则{
    返回可观察的.error(新的运行时异常(“服务2错误”);
    }
    }
    }
    

    不要使用可变对象。始终确保发出的值是不可变的,否则您会遇到麻烦。

    是的。我明白了,在现实生活中,如果两个项目来自同一个来源,那么它们不应该在同一时刻发出。但在我的情况下,我似乎不需要取消任何开关,即使它提供适当的发射时间工作。i、 我不需要开关~
    public class So65193002 {
          @Test
          void so() {
            TestScheduler testScheduler = new TestScheduler();
            Service1 service1 = new Service1(testScheduler);
            Service2 service2 = new Service2(testScheduler);
        
            Observable<Response> service1Call = service1.call(1);
        
            Observable<Response> combined =
                service1Call.concatMapEagerDelayError(
                    x -> {
                      return service2
                          .call(x.value)
                          .onErrorReturn(
                              (ex) -> {
                                x.warning = ex.getMessage();
                                return x; // returns at least service1 result
                              });
                    },
                    true);
        
            TestObserver<Response> test = combined.test();
        
            testScheduler.advanceTimeBy(1, TimeUnit.HOURS);
        
            test.assertValueCount(4)
                .assertValueAt(
                    0,
                    r -> {
                      assertThat(r.value).isEqualTo(1);
                      assertThat(r.warning).isNotEmpty();
                      return true;
                    })
                .assertValueAt(
                    1,
                    r -> {
                      assertThat(r.value).isEqualTo(200);
                      assertThat(r.warning).isNull();
                      return true;
                    })
                .assertValueAt(
                    3,
                    r -> {
                      assertThat(r.value).isEqualTo(400);
                      assertThat(r.warning).isNull();
                      return true;
                    });
          }
        }
    
    class Response {
      public Integer value;
      public String warning;
    
      public Response(Integer value) {
        this.value = value;
      }
    
      @Override
      public String toString() {
        return "Response{" + "value=" + value + ", warning='" + warning + '\'' + '}';
      }
    }
    
    class Service1 {
      private final Scheduler scheduler;
    
      Service1(Scheduler scheduler) {
        this.scheduler = scheduler;
      }
    
      public Observable<Response> call(int arg) {
        return Observable.just(
                new Response(1), //
                new Response(2),
                new Response(3),
                new Response(4))
            .delay(100, TimeUnit.MILLISECONDS, scheduler);
      }
    }
    
    class Service2 {
      private final Scheduler scheduler;
    
      Service2(Scheduler scheduler) {
        this.scheduler = scheduler;
      }
    
      public Observable<Response> call(int arg) {
        if (arg % 2 == 0) {
          return Observable.just(new Response(100 * arg)).delay(10, TimeUnit.MILLISECONDS, scheduler);
    
        } else {
          return Observable.error(new RuntimeException("service 2 error"));
        }
      }
    }