精讲响应式webclient第1篇-响应式非阻塞IO与基础用法
笔者在之前已经写了一系列的关于RestTemplate的文章,如下:
- 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用
- 精讲RestTemplate第2篇-多种底层HTTP客户端类库的切换
- 精讲RestTemplate第3篇-GET请求使用方法详解
- 精讲RestTemplate第4篇-POST请求方法使用详解
- 精讲RestTemplate第5篇-DELETE、PUT等请求方法使用详解
- 精讲RestTemplate第6篇-文件上传下载与大文件流式下载
- 精讲RestTemplate第7篇-自定义请求失败异常处理
- 精讲RestTemplate第8篇-请求失败自动重试机制
- 精讲RestTemplate第9篇-如何通过HTTP Basic Auth认证
- 精讲RestTemplate第10篇-使用代理作为跳板发送请求
RestTemplate
作为spring-web项目的一部分,在Spring 3.0版本开始被引入。根据Spring官方文档及源码中的介绍,RestTemplate在将来的版本中它可能会被弃用, 作为替代,Spring官方已在Spring 5中引入了WebClient作为非阻塞式Reactive HTTP客户端。
一、什么是响应式非阻塞IO
在开始为大家介绍webClient之前有必要为大家介绍一下响应式非阻塞IO与传统IO之前的区别。我们先留下一个问题:WebClient发送与接收单个HTTP请求比RestTemplate更快么?答案是否定的。 看到这里有的同学已经蒙了,既然webClient没有更快,那官方为什么还推荐使用它?听我往下讲。
1.1.传统阻塞式IO模型
笔者用相对通俗的话为大家说明一下阻塞IO与非阻塞IO之间的区别。我们以软件开发团队的工作方式来做一个比喻。作为软件开发人员,我们肯定知道软件开发的基本流程:
- 项目立项与可行性研究
- 需求分析与设计
- 代码开发
- 迭代测试
- 上线及配置管理、运维
在以Spring MVC或者struct为代表的框架都是基于sevlet的,其底层IO模型是阻塞IO模型。这种模型就好像你是公司的一个开发人员,上面的所有的5项工作全都由你一个人完成。如果公司有10个人,最多就只能同时进行10个需求。客户需求增多了也没有办法,只能让他们等着。如下图:一个请求占用一个线程,当线程池内的线程都被占用后新来的请求就只能等待。
1.2.响应式IO模型
spring 社区为了解决Spring MVC的阻塞模型在高并发场景下的性能瓶颈的问题,推出了Spring WebFlux,WebFlux底层实现是久经考验的netty非阻塞IO通信框架。该框架的请求处理与线程交互关系图如下:
boosGroup用于Accetpt连接建立事件并分发请求, workerGroup用于处理I/O读写事件。netty我就不细说了,还是用通俗的方式给大家讲一下:如果通俗的将上图中的各个任务池、线程池的组合比做一个软件开发公司,那么:
- 项目立项及可研,由公司项目经理及顾问来完成
- 需求分析与设计,由产品经理和架构师来完成
- 代码研发,由项目经理带领开发人员来完成
- 迭代测试,由测试团队来完成
- 上线及配置管理、运维,可能由专门的devops团队来完成
这样一个公司内的所有人完成分工,就能在有限的资源的情况下,去接触更多的客户,谈更多的需求,合理的分配人力资源,达到并发处理能力最大化的极限水平。相比于一个员工从头到位的负责一个项目,它的组织性更强,分工更明确,合理的利用空闲资源,专业的人最专业的事。 这种人力资源的合理利用及组织方式和非阻塞IO模型有异曲同工之处,通过合理的将请求处理线程及任务进行分类,合理的利用系统的内存、CPU资源,达到单位时间内处理能力的最大化就是异步非阻塞IO的核心用意! 回到上文给大家留下的问题,webClient处理单个HTTP请求的响应时长并不比RestTemplate更快,但是它处理并发的能力更强。 所以响应式非阻塞IO模型的核心意义在于:提高了单位时间内有限资源下的服务请求的并发处理能力,而不是缩短了单个服务请求的响应时长。
二、WebClient 的优势
上文为大家介绍完IO模型之后,我想大家已经可以明白了。与RestTemplate相比,WebClient优势如下:
- 非阻塞响应式IO,单位时间内有限资源下支持更高的并发量
- 支持使用Java 8 lambda表达式函数
- 同时支持同步、异步与Streaming流式传输场景
三、项目引入WebClient
使用WebClient需要引入如下的Jar(可以在包含spring
-boot-starter-web
的Spring Boot项目中引入)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
那么问题又来了,熟悉Spring 开发的朋友应该都知道。spring-boot-starter-webflux和spring-boot-starter-web代表的是两套技术栈
- spring-boot-starter-web可以实现目前比较成熟的基于servlet技术栈的Spring Boot应用
- spring-boot-starter-webflux可以实现的是底层基于netty的响应式编程的技术栈的Spring Boot应用
二者可以共存么?答案是:
- 作为服务端实现Spring Boot应用而言,二者在应用角度当然是不能共存的。截止20200820我写稿的时间,如果在一个项目里面将二者都引入了,开发服务端应用其实使用的还是spring-boot-starter-web的基于servlet的技术栈。
- 作为HTTP客户端而言,如果我们只是要使用WebClient。无论怎样,引入
spring-boot-starter-webflux
就对了。
四、WebClient 实例创建与基础用法
创建WebClient有如下三种方式,我们来一一为大家介绍。
WebClient.create()
-
WebClient.create(String baseUrl)
:指定了baseUrl,使用该客户端发送请求都基于baseUrl -
WebClient.builder()
返回一个WebClient.Builder,该对象可以做链式调用,传递更多的参数。
为了方便后续开发测试,首先介绍一个网站给大家。JSONPlaceholder是一个提供免费的在线REST API的网站,我们在开发时可以使用它提供的url地址测试下网络请求以及请求参数。或者当我们程序需要获取一些模拟数据、模拟图片时也可以使用它。
4.1. WebClient.create()
创建WebClient发送GET请求,接收String类型单个Mono对象(Mono英文:单声道、单体)。
public class SimpleTest {
@Test
void testSimple() {
WebClient webClient = WebClient.create();
Mono<String> mono = webClient
.get() // 发送GET 请求
.uri("http://jsonplaceholder.typicode.com/posts/1") // 请求路径
.retrieve() // 获取响应结果
.bodyToMono(String.class); //响应数据类型转换
System.out.println("=====" + mono.block());
}
}
mono.block()方法仍然是阻塞式的数据响应接收方式,响应式的编程方法我们后面文章会为大家介绍。
4.2.WebClient.create(String baseUrl)
上面使用create()无参方法,在指定请求uri时每次都要指定完整的HTTP服务路径,如"http://jsonplaceholder.typicode.com/posts/1"。使用WebClient.create(String baseUrl)
可以统一指定一个baseUrl,这样请求指定请求uri时,可以省略baseUrl部分,如"/posts/1"。
private WebClient webClient = WebClient.create("http://jsonplaceholder.typicode.com");
@Test
void testBaseUrl(){
Mono<String> mono = webClient
.get()
.uri("/posts/1") // 请求路径,注意省略了baseurl部分
.retrieve()
.bodyToMono(String.class);
System.out.println("=====" + mono.block());
}
上面代码请求结果,和4.1结果是一样的
4.3.WebClient.builder()
使用builder()创建WebClient对象,可以一次性传递的参数内容就更加丰富了。cookies、headers等信息都可以使用builder来传递。 场景:比如你请求的服务端使用JWT token,每次请求都需要传递token。如果每次请求都单独去创建一个WebClient,然后指定Token,那就麻烦了。我们可以使用builder在WebClient实例化的时候,统一设置Token。
private WebClient webClient = WebClient
.builder()
.defaultHeader("JWT-Token", "xxxyyy3fsfsfsff-fjdskfa")
.build();
支持的可选配置如下:
-
uriBuilderFactory
: 自定义UriBuilderFactory灵活配置使用Url -
defaultHeader
: 为HTTP请求设置Headers请求头 -
defaultCookie
: 为HTTP请求设置Cookies -
defaultRequest
: 自定义Http Request -
filter
: 为HTTP请求增加客户端过滤器 -
exchangeStrategies
: HTTP 读写信息自定义 -
clientConnector
: HTTP客户端连接器设置
喜欢 (0)or分享 (0)
- C++ 类之间的互相调用
- javascript设计模式 -- 工厂模式
- Google VR技术大揭秘
- Android入门之数据传递
- Swift 3.0介绍
- C++判断char*的指向
- Linux基础(day18)
- Spring Boot搭建Web应用
- 5.7 vim实践
- Effective Modern C++翻译(7)-条款6:当auto推导出意外的类型时,使用显式的类型初始化语义
- 2.3 ls命令
- Effective Modern C++翻译(6)-条款5:auto比显示的类型声明要更好
- 大白话-prototype属性
- Effective Modern C++翻译(5)-条款4:了解如何观察推导出的类型
- HTML 教程
- HTML 简介
- html div 标签介绍
- html span 标签介绍
- html a 超链接标签
- HTML Br换行标签介绍
- HTML P段落标签介绍
- HTML br与p标签区别
- Html H 标题标签
- html px em pt长度单位
- HTML form 标签
- HTML radio 单选框
- HTML B 加粗标签
- HTML strong加粗粗体标签
- HTML em 强调标签
- HTML i 斜体标签
- HTML u下划线标签
- HTML s 删除线标签
- Html img 图片标签
- Html上标注sup与下标注sub标签
- HTML nobr 禁止换行标签
- HTML hr 水平线标签
- HTML label 标签
- HTML input 标签
- HTML textarea 标签
- HTML select下拉列表标签
- HTML checkbox 多选框
- HTML font color 标签
- HTML iframe 框架标签
- HTML Table 表格
- HTML dl dt dd 标签
- HTML ol li有序列表标签
- HTML ul li 无序列表标签
- HTML 注释
- CSS 教程
- CSS 简介
- CSS 语法
- CSS Id 和 Class选择器
- CSS 样式的创建
- CSS background 背景介绍
- CSS 文本样式
- CSS font 字体
- CSS A 链接
- CSS ul ol列表样式
- CSS TABLE 样式
- CSS 框模型
- CSS border 边框
- CSS Outlines 轮廓
- CSS 外边距 Margin
- CSS Padding 内边距
- CSS 分组和嵌套选择器
- CSS 尺寸 (Dimension)
- CSS Display 属性
- CSS Position 定位
- CSS Float 浮动
- CSS 水平对齐(Horizontal Align)
- CSS 组合选择符
- CSS 伪类
- CSS 伪元素
- CSS 导航栏
- CSS 下拉菜单
- CSS 图片廊
- CSS 图像透明/不透明
- CSS sprite 图像拼合技术
- CSS 媒体类型
- CSS 属性选择器
- CSS 实例