Spring Boot WebClient.Builder bean在传统servlet多线程应用程序中的使用

Spring Boot WebClient.Builder bean在传统servlet多线程应用程序中的使用,spring,spring-boot,spring-webflux,Spring,Spring Boot,Spring Webflux,我希望有一个http客户端从Spring Boot非反应式应用程序调用其他微服务。由于RestTemplate将被弃用,我尝试使用WebClient.Builder和WebClient。虽然我不确定线程的安全性。例如: @Service public class MyService{ @Autowired WebClient.Builder webClientBuilder; public VenueDTO serviceMethod(){ //!!!

我希望有一个http客户端从Spring Boot非反应式应用程序调用其他微服务。由于RestTemplate将被弃用,我尝试使用WebClient.Builder和WebClient。虽然我不确定线程的安全性。例如:

@Service
public class MyService{
    @Autowired
    WebClient.Builder webClientBuilder;

    public VenueDTO serviceMethod(){
        //!!! This is not thread safe !!!
        WebClient webClient = webClientBuilder.baseUrl("http://localhost:8000").build();

        VenueDTO venueDTO = webClient.get().uri("/api/venue/{id}", bodDTO.getBusinessOutletId()).
                retrieve().bodyToMono(VenueDTO.class).
                blockOptional(Duration.ofMillis(1000)).
                orElseThrow(() -> new BadRequestException(venueNotFound));
                return VenueDTO;
    }
}
本例中的serviceMethod()将从几个线程调用,webClientBuilder是一个bean实例。WebClient.Builder类包含state:baseUrl,这似乎不是线程安全的,因为很少有线程可以同时调用此状态更新。同时,WebClient本身似乎是线程安全的,正如在

我应该使用中提到的WebClient.Builderbean吗

SpringBoot为您创建并预配置WebClient.Builder;信息技术 强烈建议将其注入组件中并使用它 创建WebClient实例

我看到的一个解决方案是创建WebClient,而不将任何状态传递给构建器,因此:

WebClient webClient = webClientBuilder.baseUrl("http://localhost:8000").build();
我会:

WebClient webClient = webClientBuilder.build();
并在uri方法调用中传递带有协议和端口的完整url:

webClient.get().uri("full url here", MyDTO.class)

在我的情况下使用它的正确方法是什么?

你说得对,
WebClient.Builder
不是线程安全的

SpringBoot正在创建
WebClient.Builder
作为一个原型bean,因此您将为每个注入点获得一个新实例。在您的情况下,您的组件在我看来似乎有点奇怪

应该是这样的:

@Service
public class MyService{

    private final WebClient webClient;

    public MyService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("http://localhost:8000").build();
    }

    public VenueDTO serviceMethod(){
        VenueDTO venueDTO = webClient.get().uri("/api/venue/{id}", bodDTO.getBusinessOutletId()).
                retrieve().bodyToMono(VenueDTO.class).
                blockOptional(Duration.ofMillis(1000)).
                orElseThrow(() -> new BadRequestException(venueNotFound));
                return VenueDTO;
    }
}
现在我想这是一个代码片段,您的应用程序可能有不同的约束

如果您的应用程序需要经常更改基本URL,那么我认为您应该停止在生成器上配置它,并按照您的问题中所述传递完整的URL。如果您的应用程序有其他需要(用于身份验证的自定义头等),那么您也可以在构建器上或在每个请求的基础上这样做

通常,您应该尝试为每个组件构建一个
WebClient
实例,因为为每个请求重新创建它是非常浪费的


如果您的应用程序有非常特定的约束,并且确实需要创建不同的实例,那么您可以随时调用
webClientBuilder.clone()
,获取一个新的构建器实例,您可以对其进行修改,而不必担心线程安全问题。

My@Service需要通过http与少数微服务进行对话。我知道它们的功能,但收件人可以在运行时更改。所以我想我可以在构造函数中注入WebClient.Builder,并在WebClient.Builder中构建我的WebClient实例,而无需baseUrl调用。在这种情况下,我似乎只需要一个WebClient实例来支持所有远程微服务。对吧?对,;baseUrl只是一种方便,以防所有请求都发送到同一主机。这不是您的情况,所以您应该从构建器阶段删除它,只需使用完整的URLsJust checked WebClient发出请求。构建器是原型bean。我会按照你建议的路线走。我无法在找到我的问题的所有答案,我尝试在我的服务方法中使用builder,因为我最初认为WebClient不是线程安全的(虽然它看起来实际上是)。非常感谢。从Spring引导将WebClient.Builder创建为原型bean的过程中,您从哪里获得了信息?我刚刚调试了一个有两个客户端的应用程序,并且注入了相同的构建器实例!我现在正在寻找解决这个问题的方法…这里是自动配置的链接:如果您有一个示例应用程序显示这个问题,您可以提交一个关于Spring Boot项目的问题。