我想让一个http客户端从Spring Boot notactive 应用程序调用其他微服务。由于将不使用RestTemplate,因此我尝试使用WebClient.Builder和WebClient。虽然我不确定线程安全性。例如:
A: 390.
a: 390.
z: -1.
j: 530.
此示例中的serviceMethod()将从几个线程中调用,并且webClientBuilder是单个bean实例。 WebClient.Builder类包含状态:baseUrl,这似乎不是线程安全的,因为很少有线程可以同时调用此状态更新。同时,如Right way to use Spring WebClient in multi-thread environment
的回答中所述,WebClient本身似乎是线程安全的我应该使用https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-webclient.html
中提到的WebClient.Builder beanSpring Boot为您创建并预配置了WebClient.Builder;它 强烈建议将其注入您的组件中并用于 创建WebClient实例。
我看到的一种解决方法是创建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;
}
}
我会做的:
WebClient webClient = webClientBuilder.baseUrl("http://localhost:8000").build();
并在uri方法调用中传递带有协议和端口的完整url:
WebClient webClient = webClientBuilder.build();
在我的情况下使用它的正确方法是什么?
答案 0 :(得分:2)
您是对的,WebClient.Builder
不是线程安全的。
Spring Boot正在创建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()
并获取一个可以更改的构建器新实例,而不会出现线程安全问题。