Java SpringWebFlux-并行发送http请求

Java SpringWebFlux-并行发送http请求,java,spring-boot,spring-webflux,spring-webclient,Java,Spring Boot,Spring Webflux,Spring Webclient,关于如何在Spring Webflux和Spring WebClient的上下文中并行发送http请求的问题,请: 我有一个非常简单的场景,从一个请求(步骤1)开始,我向一个外部API发出第一个请求 步骤2,根据步骤1中第一个请求的结果,我必须进行N次请求(假设三次请求与外部API并行) 因为它们彼此没有依赖关系,所以我希望这是并行进行的 然而,我确实需要“等待”每个人的结果,这样我才能进行最后一步计算 如何在Spring Webflux Spring WebClient中实现这一点 我写了以下

关于如何在Spring Webflux和Spring WebClient的上下文中并行发送http请求的问题,请:

我有一个非常简单的场景,从一个请求(步骤1)开始,我向一个外部API发出第一个请求

步骤2,根据步骤1中第一个请求的结果,我必须进行N次请求(假设三次请求与外部API并行)

因为它们彼此没有依赖关系,所以我希望这是并行进行的

然而,我确实需要“等待”每个人的结果,这样我才能进行最后一步计算

如何在Spring Webflux Spring WebClient中实现这一点

我写了以下内容,并联系了我发送请求的每一个外部方

@RestController
public class QuestionController {

    private WebClient webClient;

    @Autowired
    public QuestionController(WebClient webClient) {
        this.webClient = WebClient.create("http://some-service:8111/getPriceForFoo");
    }

    @GetMapping("/question")
    public Mono<String> question(@RequestParam(value = "foo", required = false) String foo) {
        //Step 1 - get the price through an API to some service
        Mono<String> priceMustBeComputedFirst = firstComputePriceBySendingRequest(foo).cache();

        // this step can be computed in parallel with companyB and companyC
        Mono<String> interestToBuyFromCompanyA = areYouInterestedToBuyCompanyA(priceMustBeComputedFirst);
        // this step can be computed in parallel with companyA and companyC
        Mono<String> interestToBuyFromCompanyB = areYouInterestedToBuyCompanyB(priceMustBeComputedFirst);
        // this step can be computed in parallel with companyA and companyB
        Mono<String> interestToBuyFromCompanyC = areYouInterestedToBuyCompanyC(priceMustBeComputedFirst);

        return Mono.zip(interestToBuyFromCompanyA, interestToBuyFromCompanyB, interestToBuyFromCompanyC).map(tuple3 -> computeSomethingAtTheEnd(tuple3.getT1(), tuple3.getT2(), tuple3.getT3()));
    }

    private Mono<String> firstComputePriceBySendingRequest(String foo) {
        return webClient.mutate().baseUrl("http://some-service:8111/getPriceForFoo").build().get().uri("/{foo}", foo).retrieve().bodyToMono(String.class);
    }

    private Mono<String> areYouInterestedToBuyCompanyA(Mono<String> priceMustBeComputedFirst) {
        return priceMustBeComputedFirst.flatMap(product ->  webClient.mutate().baseUrl("http://companyA/areYouInterestedToBuy").build().get().uri("/{product}", product).retrieve().bodyToMono(String.class));
    }

    private Mono<String> areYouInterestedToBuyCompanyB(Mono<String> priceMustBeComputedFirst) {
        return priceMustBeComputedFirst.flatMap(product ->  webClient.mutate().baseUrl("http://companyB/areYouInterestedToBuy").build().get().uri("/{product}", product).retrieve().bodyToMono(String.class));
    }

    private Mono<String> areYouInterestedToBuyCompanyC(Mono<String> priceMustBeComputedFirst) {
        return priceMustBeComputedFirst.flatMap(product ->  webClient.mutate().baseUrl("http://companyC/areYouInterestedToBuy").build().get().uri("/{product}", product).retrieve().bodyToMono(String.class));
    }

    private String computeSomethingAtTheEnd(String t1, String t2, String t3) {
        return "some computation based on each company s response";
    }

}

@RestController
公共类问题控制器{
私人网络客户端网络客户端;
@自动连线
公共问题控制器(网络客户端网络客户端){
this.webClient=webClient.create(“http://some-service:8111/getPriceForFoo");
}
@GetMapping(“/问题”)
公共单声道问题(@RequestParam(value=“foo”,required=false)字符串foo){
//步骤1-通过API获取某项服务的价格
Mono price mustbecomputefirst=firstComputePriceBySendingRequest(foo.cache();
//此步骤可以与companyB和companyC并行计算
Mono interestToBuyFromCompanyA=您是否有兴趣购买CompanyA(价格必须先计算);
//此步骤可与companyA和companyC并行计算
Mono interestToBuyFromCompanyB=您是否有兴趣购买CompanyB(价格必须先计算);
//此步骤可与companyA和companyB并行计算
Mono InterestToBuyFromCompany=您是否有兴趣购买Company(价格必须先计算);
返回Mono.zip(interestToBuyFromCompanyA、interestToBuyFromCompanyB、InterestToBuyFromCompanyYC).map(tuple3->ComputesOmethingEnd(tuple3.getT1()、tuple3.getT2()、tuple3.getT3());
}
专用Mono firstComputePriceBySendingRequest(字符串foo){
返回webClient.mutate().baseUrl(“http://some-service:8111/getPriceForFoo“”.build().get().uri(“/{foo}”,foo.retrieve().bodytomino(String.class);
}
私人Mono是否有兴趣购买公司A(Mono价格必须先计算){
返回价格必须首先计算。flatMap(产品->webClient.mutate().baseUrl(“http://companyA/areYouInterestedToBuy“”.build().get().uri(“/{product}”,product).retrieve().bodytomino(String.class));
}
私人Mono是否有兴趣购买公司B(Mono价格必须先计算){
返回价格必须首先计算。flatMap(产品->webClient.mutate().baseUrl(“http://companyB/areYouInterestedToBuy“”.build().get().uri(“/{product}”,product).retrieve().bodytomino(String.class));
}
私人Mono是否有兴趣购买公司C(Mono价格必须先计算){
返回价格必须首先计算。flatMap(产品->webClient.mutate().baseUrl(“http://companyC/areYouInterestedToBuy“”.build().get().uri(“/{product}”,product).retrieve().bodytomino(String.class));
}
私有字符串ComputesOmethingEnd(字符串t1、字符串t2、字符串t3){
返回“基于每个公司的响应进行一些计算”;
}
}
出于某种原因,我目前所做的似乎是循序渐进的。我从每家公司得到了时间戳,事实上,A公司在B公司之前收到请求。B公司在C公司之前收到请求,等等,我相信这是非常顺序的

我也在我的终端添加了日志,看起来也是顺序的

这个代码真的是连续的吗

是因为Mono.zip吗

如何实现这种并行性


谢谢你

你的想法是对的。根据您的代码,这些代码将并发执行。需要考虑的两件事:1。价格api被调用了三次,我认为这不是你的意图。考虑在单声道上使用高速缓存操作符,因此只调用一次。2.不要为每个请求创建新的WebClient。使用构造函数中创建的单个实例。如果您执行上述所有操作,可能会得到更好的结果。尽管如此,如果您同时调用的API非常快,那么您可能不会从中受益很多,而且它们可能看起来是连续的。您好,Martin,您是对的。我确实按照你的建议使用了缓存,还创建了一个WebClient,我对其进行了修改。谢谢然而,我仍然看到一个非常连续的执行过程:'(顺序执行之间的差异有多大?我的意思是,如果它们没有达到完全相同的毫秒,这并不意味着它们是顺序执行的。此外,通过改变webclient,您仍然可以为每个请求创建一个新的webclient。您应该在控制器中创建与调用的API数量相同的webclient,或者创建一个单独的webclient实例,并在请求时设置url,而不改变客户端。