在本次演示中,团队将解释其中的一些要点。Rossen StoyanchevSpring
WebClient将使用有限数量的线程 ( 每个内核 2 个线程,在我的本地计算机上总共使用 ) 来处理应用程序中的所有请求及其响应。因此,如果您的应用程序接收并向外部服务器发出一个请求,则将以/方式处理所有这些线程的请求。12 threads100 requestsWebClientnon-blockingasynchronous
当然,正如你所提到的,一旦你调用了你的原始线程,你的原始线程就会阻塞,所以总共需要100个线程+ 12个线程来处理这些请求。但请记住,这 12 个线程的大小不会随着您发出更多请求而增加,并且它们不会执行 I/O 繁重的工作,因此这不像是生成线程来实际执行请求或使它们以每个请求线程的方式保持忙碌。block112 threadsWebClient
我不确定当线程处于其下方时,它的行为是否与通过阻塞调用时的行为相同 - 在我看来,在前者中,线程应该等待调用完成,而在后面的线程应该处理工作,所以也许那里有区别。blockRestTemplateinactiveNIOI/O
如果您开始使用这些好东西,例如处理相互依赖的请求或并行处理多个请求,这将变得有趣。然后肯定会有一个优势,因为它将使用相同的12个线程执行所有并发操作,而不是每个请求使用一个线程。reactorWebClient
例如,请考虑以下应用程序:
@SpringBootApplication
public class SO72300024 {
    private static final Logger logger = LoggerFactory.getLogger(SO72300024.class);
    public static void main(String[] args) {
        SpringApplication.run(SO72300024.class, args);
    }
    @RestController
    @RequestMapping("/blocking")
    static class BlockingController {
        @GetMapping("/{id}")
        String blockingEndpoint(@PathVariable String id) throws Exception {
            logger.info("Got request for {}", id);
            Thread.sleep(1000);
            return "This is the response for " + id;
        }
        @GetMapping("/{id}/nested")
        String nestedBlockingEndpoint(@PathVariable String id) throws Exception {
            logger.info("Got nested request for {}", id);
            Thread.sleep(1000);
            return "This is the nested response for " + id;
        }
    }
    @Bean
    ApplicationRunner run() {
        return args -> {
            Flux.just(callApi(), callApi(), callApi())
                    .flatMap(responseMono -> responseMono)
                    .collectList()
                    .block()
                    .stream()
                    .flatMap(Collection::stream)
                    .forEach(logger::info);
            logger.info("Finished");
        };
    }
    private Mono<List<String>> callApi() {
        WebClient webClient = WebClient.create("http://localhost:8080");
        logger.info("Starting");
        return Flux.range(1, 10).flatMap(i ->
                        webClient
                                .get().uri("/blocking/{id}", i)
                                .retrieve()
                                .bodyToMono(String.class)
                                .doOnNext(resp -> logger.info("Received response {} - {}", I, resp))
                                .flatMap(resp -> webClient.get().uri("/blocking/{id}/nested", i)
                                        .retrieve()
                                        .bodyToMono(String.class)
                                        .doOnNext(nestedResp -> logger.info("Received nested response {} - {}", I, nestedResp))))
                .collectList();
    }
}
如果运行此应用,可以看到所有 30 个请求都由相同的 12 个(在我的计算机中)线程立即并行处理。 如果你认为你可以从逻辑中的这种并行性中受益,那么可能值得一试。Neat!WebClient
如果没有,虽然考虑到上述原因,我实际上不会担心“额外的资源支出”,但我认为不值得为此添加整个依赖关系 - 除了额外的包袱之外,在日常操作中,推理和调试以及模型应该简单得多。reactor/webfluxRestTemplatethread-per-request
当然,正如其他人所提到的,您应该运行负载测试以获得适当的指标。