笔者在之前已经写了一系列的关于RestTemplate的文章,如下:

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客户端连接器设置

欢迎关注我的博客,里面有很多精品合集

  • 本文转载注明出处(必须带连接,不能只转文字):字母哥博客

觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。

精讲响应式webclient第1篇-响应式非阻塞IO与基础用法的更多相关文章

  1. 精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方法详解

    本文是精讲响应式WebClient第2篇,前篇的blog访问地址如下: 精讲响应式webclient第1篇-响应式非阻塞IO与基础用法 在上一篇文章为大家介绍了响应式IO模型和WebClient的基本 ...

  2. 精讲响应式WebClient第3篇-POST、DELETE、PUT方法使用

    本文是精讲响应式WebClient第3篇,前篇的blog访问地址如下: 精讲响应式webclient第1篇-响应式非阻塞IO与基础用法 精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方 ...

  3. 精讲响应式WebClient第4篇-文件上传与下载

    本文是精讲响应式WebClient第4篇,前篇的blog访问地址如下: 精讲响应式webclient第1篇-响应式非阻塞IO与基础用法 精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方 ...

  4. 精讲响应式WebClient第5篇-请求超时设置与异常处理

    本文是精讲响应式WebClient第5篇,前篇的blog访问地址如下: 精讲响应式webclient第1篇-响应式非阻塞IO与基础用法 精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方 ...

  5. 精讲响应式WebClient第6篇-请求失败自动重试机制,强烈建议你看一看

    本文是精讲响应式WebClient第6篇,前篇的blog访问地址如下: 精讲响应式webclient第1篇-响应式非阻塞IO与基础用法 精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方 ...

  6. Python学习-day10(番外篇) 阻塞IO 非阻塞IO 同步IO 异步IO

    这个章节的内容是关于IO的概念,谈一谈什么是 阻塞IO 非阻塞IO 同步IO 异步IO.以下摘要是我对这四种IO的一个形象理解. 场景是去去银行办理业务.节点有三个,1)到银行提交申请:2)取号:3) ...

  7. 什么是阻塞式和非阻塞io流?

    阻塞IO:socket 的阻塞模式意味着必须要做完IO 操作(包括错误)才会返回. 非阻塞IO:非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方式来判断具体操作是否成功. 两者区别: 所谓阻塞 ...

  8. [日常摘要] -- 阻塞IO与非阻塞IO篇

    NIO操作过程 非阻塞读/写操作 读-- 从通道读取数据到buffer,同时可以继续做别的事情,但数据都到buffer之后,线程再继续处理数据 写-- 一个线程请求写入一些数据到某通道,但不需要等待它 ...

  9. 小书MybatisPlus第7篇-代码生成器的原理精讲及使用方法

    本文是本系列文章的第七篇,前6篇访问地址如下: 小书MybatisPlus第1篇-整合SpringBoot快速开始增删改查 小书MybatisPlus第2篇-条件构造器的应用及总结 小书Mybatis ...

随机推荐

  1. 面试题四十三:在1~n整数中1出现的次数

    方法一:直观来看,遍历1到n,每个数去做%10的循环判断 int Number1_B_1toN( int n){ int sum=0; for(int i=1;i<=n;i++){ int k= ...

  2. 了不起的Node.js 5/16

    Chapter 1 安装 1.Node.js的设计理念之一,只维护较少量的依赖,这使得安装node.js变得非常简单. 2.执行文件console.log没问题,但是node执行http静态服务器的时 ...

  3. Redis 的 KEYS 命令不能乱用啊

    KESY 命令 时间复杂度: O(N) , 假设Redis中的键名和给定的模式的长度有限的情况下,N为数据库中key的个数. Redis Keys 命令用于查找所有符合给定模式 pattern 的 k ...

  4. PHP array_uintersect_assoc() 函数

    实例 比较两个数组的键名和键值(使用内建函数比较键名,使用用户自定义函数比较键值),并返回交集: <?phpfunction myfunction($a,$b){if ($a===$b){ret ...

  5. 5.15 牛客挑战赛40 E 小V和gcd树 树链剖分 主席树 树状数组 根号分治

    LINK:小V和gcd树 时限是8s 所以当时好多nq的暴力都能跑过. 考虑每次询问暴力 跳父亲 这样是nq的 4e8左右 随便过. 不过每次跳到某个点的时候需要得到边权 如果直接暴力gcd的话 nq ...

  6. RESTFUL风格的URL请求及参数接收

    RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义.RESTFUL适用于移动互联网厂商作为业务使能接口的场景,实现第三方OTT调用移动网络资源 ...

  7. UML科普文,一篇文章掌握14种UML图

    前言 上一篇文章写了一篇建造者模式,其中有几个UML类图,有的读者反馈看不懂了,我们今天就来解决一哈. 什么是UML? UML是Unified Model Language的缩写,中文是统一建模语言, ...

  8. 33-关键字:interface

    interface:接口 1.使用说明: * 1.接口使用interface来定义 * 2.Java中,接口和类是并列的两个结构 * 3.如何定义接口:定义接口中的成员 * 3.1 JDK7及以前:只 ...

  9. 025_go语言中的通道同步

    代码演示 package main import "fmt" import "time" func worker(done chan bool) { fmt.P ...

  10. SpringBoot+Mybatis关于开启驼峰映射的设置

    mybatis自定义的SQL语句中,如select语句,如果数据库表的字段为驼峰命名,即如img_address这样的形式,那么select语句执行的结果会变成null. 解决办法是在配置文件中加上开 ...