【问题标题】:How to consolidate the results of Mono Objects in spring WebFlux?Spring WebFlux如何合并Mono Objects的结果?
【发布时间】:2020-11-14 07:53:05
【问题描述】:

请查看使用 RestTemplate 的控制器(已添加 cmets)中的以下代码:

@GetMapping("/{courseid}")
public Course getCourseDetails(@PathVariable Long courseid) {

    // Get Course info (ID, Name, Description) from pre-populated Array List
    CourseInfo courseInfo = getCourseInfo(courseid);
    
    // Get Price info of a course from another microservice using RESTTemplate
    Price price = restTemplate.getForObject("http://localhost:8002/price/"+courseid, Price.class);
    
    // Get enrollment info of a course from another microservice using RESTTemplate
    Enrollment enrollment = restTemplate.getForObject("http://localhost:8003/enrollment/"+courseid, Enrollment.class);
    
    //Consolidate everything in to Course object and send it as response
    return new Course(courseInfo.getCourseID(), courseInfo.getCourseName(), courseInfo.getCourseDesc(), price.getDiscountedPrice(),
            enrollment.getEnrollmentOpen());
}

现在我正在尝试使用响应式编程来实现相同的目标。我现在使用来自 Web-Flux 的 WebClient 和 Mono。但是,我很困惑如何组合结果?看看下面的代码(只是使用 Mono Everywhere。其余代码保持不变)

@GetMapping("/{courseid}")
public Mono<Course> getCourseDetails(@PathVariable Long courseid) {

    // Get Course info (ID, Name, Description) from pre-populated Array List
    CourseInfo courseInfo = getCourseInfo(courseid);
    
    // Get Price info of a course from another microservice using RESTTemplate
    Mono<Price> price = webClient.get().uri("http://localhost:8002/price/{courseid}/",courseid).retrieve().bodyToMono(Price.class);
    
    // Get enrollment info of a course from another microservice using RESTTemplate
    Mono<Enrollment> inventory = webClient.get().uri("http://localhost:8003/enrollment/{courseid}/",courseid).retrieve().bodyToMono(Enrollment.class);
    
    //Question : How do we Consolidate everything and form a Mono<Course> object and send it as response?
    
}

问题 1:我们如何整合所有内容并形成 Mono 对象并将其作为响应发送?

问题2:语句“CourseInfo courseInfo = getCourseInfo(courseid);”是否导致阻塞操作?

谢谢!

【问题讨论】:

    标签: reactive-programming spring-webflux spring-reactive


    【解决方案1】:

    回答:

    问题 1:我们如何整合所有内容并形成 Mono 对象并将其作为响应发送?

    Mono.zip(..) 是您需要结合这两个结果。此图来自doc

    请注意,如果 A 或 1 之一为空,zip 将导致 empty Mono!使用switchIfEmpty/defaultIfEmpty 来防止这种情况。

    因此代码如下所示:

    @GetMapping("/{courseid}")
    public Mono<Course> getCourseDetails(@PathVariable Long courseid) {
    
        CourseInfo courseInfo = getCourseInfo(courseid);
        Mono<Price> priceMono = webClient.get().uri("http://localhost:8002/price/{courseid}/",courseid).retrieve().bodyToMono(Price.class);
        Mono<Enrollment> enrollmentMono = webClient.get().uri("http://localhost:8003/enrollment/{courseid}/",courseid).retrieve().bodyToMono(Enrollment.class);
        
        return Mono.zip(priceMono, enrollmentMono).map(t -> new Course(courseInfo.getCourseID(), courseInfo.getCourseName(), courseInfo.getCourseDesc(), t.getT1().getDiscountedPrice(),
                t.getT2().getEnrollmentOpen()));
        
    }
    
    

    现在回答:

    问题2:语句“CourseInfo courseInfo = getCourseInfo(courseid);”是否导致阻塞操作?

    既然你提到了Get Course info (ID, Name, Description) from pre-populated Array List如果它只是一个包含课程信息的内存数组,那么它不是阻塞的

    但是(正如@mslowiak 也提到的),如果getCourseInfo 包含涉及查询数据库的逻辑,请确保您没有使用阻塞的 JDBC 驱动程序。如果是这样,那么使用 Webflux 和 Reactor 就没有意义了。 如果是这样,请使用 Spring R2DBC

    【讨论】:

    • 谢谢!那就是答案。另外,您知道我们如何确定在使用响应式编程时没有阻塞操作吗?
    • @rakeshmehra 好吧,没有这样的固定规则。你可以用谷歌搜索。或者直观地找到它。例如,您可以检查 JDBC 驱动程序的底层实现是否是阻塞的,因为它在其类中的任何地方都没有对 Flux/Mono 的引用。即它没有使用 Reactor。 Java 中仅有的 2 个响应式项目是 RxJava 和 Reactor
    • 在拥有数百家小型啤酒厂的大型项目中,即使您在某处进行了一次阻塞操作,也可能会破坏反应式编程的目的。我相信很难手动找到阻塞操作。但是,似乎有一个库可以检测阻塞操作。
    • 首先,使用Spring Webflux时不能调用阻塞方法。它抛出错误。你可以试试看。所以它也不适用于旧的 jdbc 驱动程序。当您说微型啤酒厂时,我不知道您是指微服务。当调用转到不同的微服务时,您不必担心它是否是响应式的。
    • 是的,作为解决方案接受。
    【解决方案2】:
    1. restTemplate.getForObject 返回简单对象 - 在您的情况下为 PriceEnrollment。要将它们转换为 Mono,您只需 Mono.just(object),但更好的解决方案是切换到 Webclient,这是 Spring Reactive 的默认 HTTP 客户端

    2. getCourseInfo 这取决于这个方法背后的逻辑是什么。确定该方法后面是否有 JDBC 连接被阻塞。

    3. 要使用Mono&lt;Course&gt; 做出最终回复,您应该考虑一下zip operator,这将帮助您。

    例如:

    Mono<Course> courseMono = Mono.zip(price, enrollment)
            .map(tuple -> new Course(courseInfo, tuple.getT1(), tuple.getT2()));
    

    【讨论】:

    • 我打算使用 WebClient(我已经更正了问题)。但是,最后一条语句似乎不起作用。
    • 我刚刚意识到 "t.getT1()" 应该是 "t.getT1().getDiscountedPrice()" 并且有效!
    猜你喜欢
    • 2021-09-30
    • 2021-05-13
    • 2018-12-12
    • 2018-06-17
    • 2021-11-09
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    相关资源
    最近更新 更多