通过一个小案例 将webClient 和 restTemplate 做一个简单的比较。
详细的WebFlux/WebClient 和 Spring MVC/RestTemplate 性能对比请参考:https://github.com/bigbirditedu/webclient
引入SpringBoot依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
单独起一个springboot工程,编写一个服务端http接口
/**
* 服务提供方接口
*/
@RestController
public class ServerController {
@GetMapping("products")
public List<Product> getAllProducts(String type) throws InterruptedException {
List<Product> products = new ArrayList<>();
products.add(new Product(type + "A", "1", 52.67));
products.add(new Product(type + "B", "2", 86.21));
products.add(new Product(type + "C", "3", 27.94));
Thread.sleep(5000);
return products;
}
}
再编写一个客户端来调用上述http接口,分别使用restTemplate和webclient,对比耗时。
@RestController
public class ProductController {
@GetMapping("rest")
public List<Product> getProductsRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
long start = System.currentTimeMillis();
ResponseEntity<Product[]> resEntity = restTemplate.getForEntity("http://127.0.0.1:8081/products?type=rest", Product[].class);
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " restTemplate cost:" + (end - start));
List<Product> products = Arrays.asList(resEntity.getBody());
return products;
}
@GetMapping("webclient")
public Flux<Product> getProductsWebclient() {
long start = System.currentTimeMillis();
Flux<Product> productFlux = WebClient.create("http://127.0.0.1:8081/products?type=webc").get().retrieve().bodyToFlux(Product.class);
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " webclient cost:" + (end - start));
return productFlux;
}
}
public class Product {
private String name;
private String id;
private double price;
public Product() {
}
public Product(String name, String id, double price) {
this.name = name;
this.id = id;
this.price = price;
}
//省略getter、setter方法
}
注意启动时换个端口application.properties:
server.port=8081
运行结果:
reactor-http-nio-6 webclient cost:0
reactor-http-nio-4 restTemplate cost:5004
reactor-http-nio-4 webclient cost:0
reactor-http-nio-3 restTemplate cost:5002
由此可见,使用webClient在等待响应的同时不会阻塞正在执行的线程 ;这种异步编程的方式避免了线程阻塞,线程处理完一个请求紧接着可以处理下一个,能够提高系统的吞吐量;而restTemplate 这种方式是阻塞的,会一直占用当前线程资源,直到http返回响应。如果等待的请求发生了堆积,应用程序将创建大量线程,直至耗尽线程池和所有可用内存。同时伴随着频繁的CPU上下文切换,可能导致性能下降。
但是作为上述两种方式的调用者(消费者)而言,其最终获得http响应结果的耗时并未减少。比如浏览器访问上述ProductController 的两个接口时,返回数据的耗时相同。最终获取(消费)数据的地方还会等待。
使用webclient替代restTemplate的好处是可以异步等待http响应,使得线程不需要阻塞;单位时间内有限资源下支持更高的并发量。
异步非阻塞的、响应式的HTTP客户端请认准webclient !
《webclient 详细讲解》推荐 https://gitbook.cn/gitchat/activity/61ad7905aef4835642ab78f9
上文会详细讲解 webclient http 请求、与resttemplate对比等。
详细代码地址:https://github.com/bigbirditedu/webclient
webclient 官方教程:
https://www.baeldung.com/spring-5-webclient
https://projectreactor.io/docs/netty/snapshot/reference/index.html#_connection_pool_2
https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client-builder-reactor-resources