【问题标题】:Design API flow using RX Java Operators使用 RX Java 运算符设计 API 流程
【发布时间】:2017-05-07 12:26:36
【问题描述】:

我是 RX-Java 的新手,我正在尝试设计一个 API,其流程如下所述:

            Make REST call A to load data
                        |
                        |
        data not found? |  data found  
    ------------------------------------    
    |                                  |           
    |                                  |            
    |                                  |
Make REST Call B                  Load DB Data 1     
    |                                  |            
    |                                  |
    |                              _________________________
    |                             |       Parallel         |
    |                             |                        |
    |                             |                        |
    |                  (condition using DB data 1)   (condition using DB data 1)
    |                      Load REST Data C                Load DB Data 2
    |                             |                        |
    |                             |________________________|
    |                                         |
    |                                         |
Build Response                            Build Response

假设 DB 方法和服务调用返回 Observable,需要对上面使用 rx 运算符的框架流程进行一些澄清吗?

我将在下面分享阻塞伪代码:

Response = REST_Call_1(); // on error throw Exception

if (isResponseValid(response)) { // returns Boolean
    if (responseUnderReview(response)) { // validation func 
        throw Exception;
    } else {
        //db_data_1 and db_data_2 can be parallel
        db_data_1 = Load_DB_Data_1();

        // Load data_3 and data_2 based on db_data_1
        if (is_data_3_required(db_data_1)) {
            data_3 = REST_call_2();
        }
        if (is_data_2_required(db_data_1)) {
            db_data_2 = REST_call_2();
        }
        buildResponse(db_data_1, db_data_2, data_3, Response);
    }
} else {
    Response = REST_Call_3(); // on error throw Exception
    buildResponse(response);
}

我正在研究一种完整的非阻塞异步方法。

【问题讨论】:

    标签: java spring-mvc rx-java reactive-programming


    【解决方案1】:

    一般的逻辑流程可以如下:

    retrofitClient
    .loadData()...
    .onErrorResumeNext(Observable.empty()) // or handle specific errors only
    .flatMap(foundData -> 
        Observable.zip(
           database.call1(foundData),
           database.call2(foundData),
           (call1, call2) -> buildResponse(call1,call2)
        )
     )
     .switchIfEmpty(() ->
        retrofitClient
        .callB()
        .map(response -> buildResponse(response))
     )
    

    请注意,如果流程中有复杂的逻辑,我总是会尝试将其提取到单独的方法中。在您的情况下,基于 REST 调用调用数据库可能涉及一些转换 - 如果生成的逻辑超过一两行,我会将其移至单独的方法并在 RX 流中使用方法引用。

    我们的想法是让流程成为可以在单个页面中查看和解析的东西,并在方法中隐藏实现细节。

    编辑:编辑后,也许这更有意义:

    REST_Call_1()
    .filter(response -> isResponseValid(response))
    .flatMap(response ->
         isResponseUnderReview(response)
         ? Observable.error(new Exception())
         : Observable.just(response)
    )
    .flatMap(foundData -> 
        Observable.zip(
           fetchData13(foundData),
           Load_DB_Data_2(foundData),
           (data13, call2) -> buildResponse(data13.getLeft(),call2,data13.getRight())
        )
     )
     .switchIfEmpty(() ->
        REST_Call_3()
        .flatMap(response -> buildResponse(response))
     )
     .subscribe(....)
    
     private Observable<Pair<DbData1, DbData3>> fetchData13(foundData) {
        return
        Load_DB_Data_1()
        .flatMap(data1 -> is_data_3_required(data1)
            ? REST_call_2().map(data3 -> Pair.of(data1, data3))
            : Pair.of(data1, null));
     }
    

    【讨论】:

    • 解决方案的一个小问题是 switchIfEmpty 期望 Observable 类型与上游的相同。但在可能的情况下; REST_Call_3() 将返回不同类型的响应。我们如何解决这个问题?请指教。
    • 如果你看到我的解决方案,我有一个 .map(response -&gt; buildResponse(response)) 正是出于这个原因。
    • 完成,在您的原始描述中,您确实提到一切都返回 Observables,显然我错过了。
    • 啊,原来的情况不是这样的。不要接受这个答案;使用 flatMap 来确定要执行 2 个分支中的哪一个(阅读:作为 Observables 返回)。
    【解决方案2】:

    Tassos 的回答在我看来是合理的,我没有看到提到的任何其他问题,但是

    // dataSource.getItemDetails(activityId returns List<ItemDetail> and is a blocking call
    // So, I want to run it on a separate IO thread.
    return Observable.from(dataSource.getItemDetails(activityId)).observeOn(Schedulers.io());
    

    如果是这样,将阻塞的单元素调用转换为线程外调用可以如下完成:

    Observable.fromCallable(() -> yourBlockingCall())
    .subscribeOn(Schedulers.io())
    .flatMapIterable(v -> v)
    ...
    

    Observable.defer(() -> Observable.from(yourBlockingCall()))
    .subscribeOn(Schedulers.io())
    ...
    

    编辑:根据图表,我将设置以下流程:

    serviceCallA()
    .flatMap(a -> {
        if (dataFound(a)) {
            return dbCall1()
               .flatMap(db1 -> {
                   Observable o1 = shouldCallServiceC(db1) 
                        ? serviceCallC() : just(placeholderC);
                   Observable o2 = shouldCallDB2(db1) 
                        ? dbCall2() ? just(placeHolderDb2);
    
                   return zip(o1, o2, (c, d) -> createResult(c, d));   
               });
        }
        return serviceCallB()
            .map(c -> mapToResultType(c));
    });
    

    【讨论】:

    • @tassos 解决方案的一个小问题是 switchIfEmpty 期望 Observable 类型与上游的相同。但在可能的情况下; REST_Call_3() 将返回不同类型的响应。我们如何解决这个问题?请指教。
    • 将所有内容映射到联合类型(或记录类型),然后在最后将其映射回所需的结果类型。
    • 我采用了相同的方法,只是在这种情况下我需要转发上游数据'a'和'db1'以便在 createResult 中使用,因此我在 zip 中的 2 个可观察对象返回 Pair,总共携带 4 个createResult 所需的数据。工作,但使代码看起来有点复杂!谢谢。
    • 如下所示: serviceCallA() .filter(response -> isValidResponse(response)) .flatMap(response -> transformResponse(response)) .flatMap(a -> reviewValidation(a) ? dbCall1( a) : Observable.error(new Exception()) ) .flatMap(data -> Observable.zip( dbCall2(data.getLeft(), data.getRight()), CallServiceC(data.getLeft(), data.getRight( )), (a,b) -> buildResponse(a, b) )
    猜你喜欢
    • 2014-09-29
    • 2019-12-07
    • 2018-03-14
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多