什么是 WebClient 以及 Java 微服务如何使用 WebClient 进行通信?代码详细介绍
在 Java 微服务中,服务之间经常需要通过 HTTP 进行通信。Spring Boot 提供了强大的非阻塞 HTTP 客户端工具 —— WebClient,用于替代传统的 RestTemplate。
一、什么是 WebClient?
WebClient 是 Spring WebFlux 中引入的一个响应式、非阻塞的 HTTP 客户端,用于发起 HTTP 请求(GET、POST、PUT、DELETE 等)并接收响应。
🔹 属于 Spring 5 的一部分,可用于同步和异步通信
🔹 支持响应式编程(Reactor)、也可以用于传统 MVC 项目
二、WebClient 与微服务通信的作用
在微服务架构中,每个服务都是独立运行的,因此:
-
订单服务(Order)需要调用商品服务(Product)
-
用户服务(User)需要调用认证服务(Auth)
这些调用通常使用 WebClient 发出 HTTP 请求完成服务间通信。
三、依赖配置
如果你使用的是 Spring Boot 项目,需要在 pom.xml 中添加以下依赖:
<!-- Spring Boot WebFlux 提供 WebClient --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId></dependency>
四、使用 WebClient 的基本方式
1. 创建 WebClient 实例
方式一:使用默认构建器
WebClient webClient = WebClient.create();
方式二:指定 base URL
WebClient webClient = WebClient.builder() .baseUrl(\"http://product-service\") .build();
也可以注入为 Spring Bean:
@Configurationpublic class WebClientConfig { @Bean public WebClient webClient() { return WebClient.builder().build(); }}
2. 发起请求(GET / POST / PUT / DELETE)
示例 1:GET 请求获取商品列表
List<String> products = webClient.get() .uri(\"http://localhost:8081/products\") .retrieve() .bodyToFlux(String.class) .collectList() .block(); // 阻塞获取数据(适合同步场景)
示例 2:POST 请求创建订单
OrderRequest request = new OrderRequest(\"product123\", 2);OrderResponse response = webClient.post() .uri(\"http://localhost:8082/orders\") .bodyValue(request) .retrieve() .bodyToMono(OrderResponse.class) .block(); // 或异步处理
五、使用 WebClient 异步响应式编程
public Mono<List<Product>> fetchProductsAsync() { return webClient.get() .uri(\"http://product-service/products\") .retrieve() .bodyToFlux(Product.class) .collectList(); // 返回 Mono<List>}
可与 Reactor 或 Project Reactor 配合使用,构建响应式流式处理。
六、与 Spring Cloud 结合使用(服务名调用 + 负载均衡)
如果你使用了 Spring Cloud 和 Eureka 注册中心,可以通过服务名称(而不是 IP 地址)访问目标服务。
✅ 添加依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
✅ 使用 @LoadBalanced 注入 WebClient
@Bean@LoadBalancedpublic WebClient.Builder loadBalancedWebClientBuilder() { return WebClient.builder();}
调用方式:
webClientBuilder.build() .get() .uri(\"http://product-service/products\") .retrieve() .bodyToFlux(Product.class);
📌 自动通过注册中心(如 Eureka)找到服务并进行负载均衡
七、错误处理(如 404, 500)
webClient.get() .uri(\"/products/123\") .retrieve() .onStatus(HttpStatus::is4xxClientError, response -> { return Mono.error(new RuntimeException(\"Client Error\")); }) .onStatus(HttpStatus::is5xxServerError, response -> { return Mono.error(new RuntimeException(\"Server Error\")); }) .bodyToMono(Product.class);
八、总结:为什么在微服务中推荐使用 WebClient?
使用 Spring Boot + WebClient 实现 服务 A 调用服务 B 的完整示例,包括:
-
服务 B 提供商品数据(Product Service)
-
服务 A 调用服务 B 的接口获取商品信息(Order Service)
-
使用 WebClient 实现服务间通信
简洁明了的代码结构,便于实际项目应用
场景说明
[Order Service] --通过 WebClient 调用--> [Product Service]
1. 服务 B(Product Service)
1.1 创建 ProductService 应用(端口 8081)
ProductController.java
@RestController@RequestMapping(\"/products\")public class ProductController { @GetMapping(\"/{id}\") public Product getProduct(@PathVariable String id) { return new Product(id, \"Sample Product\", 99.99); }}
Product.java
public class Product { private String id; private String name; private double price; // 构造器、Getter 和 Setter 略}
2. 服务 A(Order Service)
2.1 创建 OrderService 应用(端口 8080)
WebClientConfig.java
@Configurationpublic class WebClientConfig { @Bean public WebClient webClient() { return WebClient.builder() .baseUrl(\"http://localhost:8081\") // Product Service 地址 .build(); }}
ProductClientService.java
@Servicepublic class ProductClientService { private final WebClient webClient; public ProductClientService(WebClient webClient) { this.webClient = webClient; } public Product getProductById(String productId) { return webClient.get() .uri(\"/products/{id}\", productId) .retrieve() .bodyToMono(Product.class) .block(); // 同步获取(可用异步处理替代) }}
OrderController.java
@RestController@RequestMapping(\"/orders\")public class OrderController { private final ProductClientService productClientService; public OrderController(ProductClientService productClientService) { this.productClientService = productClientService; } @GetMapping(\"/product/{id}\") public ResponseEntity<Product> getProductInfo(@PathVariable String id) { Product product = productClientService.getProductById(id); return ResponseEntity.ok(product); }}
3. 请求示例
启动两个服务后,访问:
GET http://localhost:8080/orders/product/101
返回 JSON(来自 Product Service):
{ \"id\": \"101\", \"name\": \"Sample Product\", \"price\": 99.99}
4. 提示:在注册中心 + 负载均衡下的用法
若使用 Eureka 注册中心,修改配置如下:
WebClientConfig.java
@Configurationpublic class WebClientConfig { @Bean @LoadBalanced // 使 WebClient 支持服务名调用 public WebClient.Builder webClientBuilder() { return WebClient.builder(); }}
使用服务名调用(不写 localhost)
webClientBuilder.build() .get() .uri(\"http://product-service/products/{id}\", productId) .retrieve() .bodyToMono(Product.class) .block();
5. 项目结构总结
order-service/├── config/│ └── WebClientConfig.java├── controller/│ └── OrderController.java├── service/│ └── ProductClientService.java└── model/ └── Product.java