原创文章,转载请注明原文章地址,谢谢!
Spring3.0引入了RestTemplate,但是在后来的官方源码中介绍,RestTemplate有可能在未来的版本中被弃用,所谓替代RestTemplate,在Spring5中引入了WebClient作为非阻塞式Reactive Http客户端。
传统阻塞IO模型
采用阻塞IO模式获取输入数据。每个连接都需要独立的线程,完成数据输入、业务处理、返回。传统阻塞IO模型的问题是,当并发数很大时,就要创建大量线程,占用很大的系统资源。连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read操作,造成线程资源浪费。
阻塞IO模型图响应式IO模型
SpringMVC或Struct等框架都是基于Servlet的,其底层IO模型是阻塞IO模型。Spring社区为了解决SpringMVC的阻塞模型在高并发场景下的性能瓶颈,推出了Spring WebFlux,WebFlux底层实现是久经考验的Netty非阻塞IO通信框架。其实WebClient处理单个HTTP请求的响应时长并不比RestTemplate更快,但是它处理并发的能力更强。 所以响应式非阻塞IO模型的核心意义在于,提高了单位时间内有限资源下的服务请求的并发处理能力,而不是缩短了单个服务请求的响应时长。
Netty的请求处理与线程交互的关系图与RestTemplate相比,WebClient的优势
- 非阻塞响应式IO,单位时间内有限资源下支持更高的并发量。
- 支持使用Java8 Lambda表达式函数。
- 支持同步、异步、Stream流式传输。
WebFlux使用
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
WebClient实例创建
- WebClient.create()
@Test
public void testCreate() {
Mono<String> mono = WebClient
//创建WenClient实例
.create()
//方法调用,WebClient中提供了多种方法
.method(HttpMethod.GET)
//请求url
.uri("http://localhost:8080/hello")
//获取响应结果
.retrieve()
//将结果转换为指定类型
.bodyToMono(String.class);
//block方法返回最终调用结果,block方法是阻塞的
System.out.println("响应结果:" + mono.block());
}
- WebClient.create(String baseUrl):指定baseUrl,使用该客户端发送请求都是基于baseUrl。
@Test
public void testCreateBaseUrl() {
Mono<String> mono = WebClient
//创建WenClient实例,指定基础url,所以下面uri请求的路径都是基于这个路径
.create("http://localhost:8080")
//方法调用,WebClient中提供了多种方法
.method(HttpMethod.GET)
//请求url
.uri("/hello")
//获取响应结果
.retrieve()
//将结果转换为指定类型
.bodyToMono(String.class);
//block方法返回最终调用结果,block方法是阻塞的
System.out.println("响应结果:" + mono.block());
}
- WebClient.builder():返回一个WebClient.Builder,该对象可以做链式调用,传递更多的参数。
@Test
public void testBuilder() {
Mono<String> mono = WebClient
.builder()
//配置头信息,或者其他信息
.defaultHeader("token", "123456789")
//创建WebClient实例
.build()
//方法调用,WebClient中提供了多种方法
.method(HttpMethod.GET)
//请求url
.uri("http://localhost:8080/hello")
//获取响应结果
.retrieve()
//将结果转换为指定类型
.bodyToMono(String.class);
}
支持的可选配置
uriBuilderFactory:自定义UriBuilderFactory灵活配置使用Url
defaultHeader:为HTTP请求设置Headers请求头
defaultCookie:为HTTP请求设置Cookies
defaultRequest:自定义HttpRequest
filter:为HTTP请求增加客户端过滤器
exchangeStrategies:HTTP读写信息自定义
clientConnector:HTTP客户端连接器设置
获取响应结果的方式
block()阻塞式获取响应结果
@Test
public void testMono() {
Mono<User> userMono = WebClient
.create()
.method(HttpMethod.GET)
.uri("http://localhost:8080/hello")
.retrieve()
.bodyToMono(User.class);
User user = userMono.block();
}
@Test
public void testFlux() {
Flux<User> userFlux = WebClient
.create()
.method(HttpMethod.GET)
.uri("http://localhost:8080/hello")
.retrieve()
.bodyToFlux(User.class);
List<User> users = userFlux.collectList().block();
}
使用Mono和Flux接收返回结果,一个Mono对象包含0个或1个元素,而一个Flux对象包含1个或多个元素。
subscribe()非阻塞式获取响应结果
@Test
public void testSubscribe() {
Mono<String> mono = WebClient
.create()
.method(HttpMethod.GET)
.uri("http://localhost:8080/hello")
.retrieve()
.bodyToMono(String.class);
mono.subscribe(WebClientTest::handleMonoResp);
}
//响应回调
private static void handleMonoResp(String monoResp) {
System.out.println("请求结果为:" + monoResp);
}
exchange()获取HTTP响应完整内容
@Test
public void testExchange() {
Mono<ClientResponse> clientResponseMono = WebClient
.create()
.method(HttpMethod.GET)
.uri("http://localhost:8080/hello")
.exchange();
ClientResponse clientResponse = clientResponseMono.block();
//响应头
ClientResponse.Headers headers = clientResponse.headers();
//响应状态
HttpStatus httpStatus = clientResponse.statusCode();
//响应状态码
int rawStatusCode = clientResponse.rawStatusCode();
//响应体
Mono<String> mono = clientResponse.bodyToMono(String.class);
String body = mono.block();
}
占位符传参
数字占位符
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
String url = "http://localhost:8080/user/{1}/{2}";
Mono<String> mono = WebClient.create()
.method(HttpMethod.POST)
.uri(url, list.toArray())
.retrieve()
.bodyToMono(String.class);
String result = mono.block();
}
参数名占位符
public static void main(String[] args) throws Exception {
String url = "http://localhost:8080/user/{id}/{name}";
String id = "123";
String name = "Boss";
Mono<String> mono = WebClient.create()
.method(HttpMethod.POST)
.uri(url, id, name)
.retrieve()
.bodyToMono(String.class);
String result = mono.block();
}
map传参
public static void main(String[] args) throws Exception {
String url = "http://localhost:8080/user/{id}/{name}";
Map<String, String> params = new HashMap<>();
params.put("id", "123");
params.put("name", "Boss");
Mono<String> mono = WebClient.create()
.method(HttpMethod.POST)
.uri(url, params)
.retrieve()
.bodyToMono(String.class);
String result = mono.block();
}
博客内容仅供自已学习以及学习过程的记录,如有侵权,请联系我删除,谢谢!
网友评论