如何并行进行多个Spring Webclient调用并等待结果?

时间:2019-06-05 20:52:11

标签: spring-boot kotlin spring-webflux project-reactor spring-webclient

我是Reactive编程的新手,我想并行进行两个API调用并处理结果,并返回一个简单的数组或项目列表。

我有两个函数,一个函数返回一个Flux,另一个函数返回一个Mono,然后我根据那个Mono的结果对Flux发出的项目进行非常简单的过滤逻辑。

我尝试使用zipWith,但是无论采用哪种过滤逻辑,只有一项可以使用到最后。我也尝试过block,但是在控制器内部是不允许的:/

@GetMapping("/{id}/offers")
fun viewTaskOffers(
        @PathVariable("id") id: String,
        @AuthenticationPrincipal user: UserPrincipal
) : Flux<ViewOfferDTO> {
    data class TaskOfferPair(
        val task: TaskDTO,
        val offer: ViewOfferDTO
    )

    return client.getTaskOffers(id).map {
            it.toViewOfferDTO()
        }.zipWith(client.getTask(id), BiFunction {
            offer: ViewOfferDTO, task: TaskDTO -> TaskOfferPair(task, offer)
        }).filter {
            it.offer.workerUser.id == user.id || it.task.creatorUser == user.id
        }.map {
            it.offer
        }
}
  • getTaskOffers返回OfferDTO的通量
  • getTask返回TaskDTO的Mono

如果您不能回答我的问题,请至少告诉我如何并行执行多个API调用并等待WebClient中的结果

3 个答案:

答案 0 :(得分:2)

这是并行调用的用例。

public Mono<UserInfo> fetchCarrierUserInfo(User user) {
        Mono<UserInfo> userInfoMono = fetchUserInfo(user.getGuid());
        Mono<CarrierInfo> carrierInfoMono = fetchCarrierInfo(user.getCarrierGuid());

        return Mono.zip(userInfoMono, carrierInfoMono).map(tuple -> {
            UserInfo userInfo = tuple.getT1();
            userInfo.setCarrier(tuple.getT2());
            return userInfo;
        });
    }

这里:

  • fetchUserInfo进行http调用以从其他服务获取用户信息并返回Mono
  • fetchCarrierInfo方法进行HTTP调用以从另一个服务获取carrierInfo并返回Mono
  • Mono.zip()将给定的Monos合并到一个新的Mono中,当所有给定的Monos都生成一个项目时,将满足该条件,并将其值汇总到Tuple2中。

然后,调用fetchCarrierUserInfo().block()以获得最终结果。

答案 1 :(得分:1)

您已经知道,zipWith不会对您有所帮助,因为它将产生min(a.size, b.size),如果其中之一为Mono,它将始终为1。 / p>

但是由于这两个是独立的,因此您可以将它们拆分:

val task: Mono<TaskDTO> = client.getTask(id)
val result: Flux<ViewOfferDTO> = 
task.flatMapMany {t ->
        client.getTaskOffers(id).map {offer ->
            t to offer
        }
    }.filter {
        it.second.workerUser.id == user.id || it.first.creatorUser == user.id
    }.map {
        it.second
}

请注意,如果要具有一对元素,则可以使用内置的Pair

此外,此检查没有太大意义,因为您只有Monoit.first.creatorUser

答案 2 :(得分:1)

使用repeat()将您的Mono转换为助焊剂:

client.getTask(id).cache().repeat();

这样您的代码就会变成

    return client.getTaskOffers(id).map {
        it.toViewOfferDTO()
    }.zipWith(client.getTask(id).cache().repeat(), BiFunction {
        offer: ViewOfferDTO, task: TaskDTO -> TaskOfferPair(task, offer)
    }).filter {
        it.offer.workerUser.id == user.id || it.task.creatorUser == user.id
    }.map {
        it.offer
    }