0
点赞
收藏
分享

微信扫一扫

WebClient异步HTTP客户端简述

ITWYY 2022-01-04 阅读 110
mavenjarjava

WebClient异步HTTP客户端

WebClient 的优势

WebClient是从Spring5开始提供的HTTP异步请求客户端接口,实现是DefaultWebClient。WebClient底层基于Netty的NIO框架和Reactor反应流编程框架。主要的功能是实现http的异步非阻塞请求。它相比传统RestTemplate同步阻塞请求可以更加高效利用系统资源。与RestTemplate相比,WebClient优势如下:
• 非阻塞响应式IO,单位时间内有限资源下支持更高的并发量
• 支持使用Java 8 lambda表达式函数
• 同时支持同步、异步与Streaming流式传输场景

基于使用层面的介绍

(1) 安装Spring WebFlux

(2) webclient初始化创建及配置
使用默认配置
加入自定义方式
连接、读取超时时间配置

(2) API请求过程参数配置
请求方法(post/get/put/delete…)
URI配置
请求BODY配置

请求头配置

将响应结果转化为实体(即发布者Publisher,可以异步将接收的消息通知给订阅者)

异步流处理过程的线程配置可参考(了解):
https://projectreactor.io/docs/core/release/reference/index.html#schedulers

另外,我们也可以对客户端请求调用timeout,但这是一个信号超时(Schedulers),而不是HTTP连接、读/写或响应超时;是Mono/Flux发布者超时时间。

官方文档详情:https://docs.spring.io/spring-framework/docs/5.3.3/reference/html/web-reactive.html#webflux-client
基本用法:https://www.baeldung.com/spring-5-webclient
配置也可以参考:Spring5的WebClient使用详解 | KL博客

从命令式到反应式(声明式)编程

1 Reactive Stream规范
反应式流编程规范是JDK9开始提出的一种接口设计模型。反应流的主要目标是管理跨异步边界的流数据交换,即将元素传递到另一个线程或线程池,同时确保接收方不会被迫缓冲任意数量的数据。反应式编程是处理异步数据流的过程,其中应用程序在数据项出现时对其作出反应。数据流本质上是随时间发生的一系列数据项。与对内存中的数据进行迭代相比,该模型的内存效率更高,因为数据是作为流处理的。
响应式流编程概念:https://www.reactive-streams.org/
https://community.oracle.com/tech/developers/discussion/4418040/reactive-programming-with-jdk-9-flow-api
2 Reactive Stream规范实现之Project Reactor
Project Reactor是一个运行在JVM上的反应式编程基础库,以“背压”的形式管理数据处理,提供了可组合的异步序列API Flux和Mono。Reactor是Pivotal提供的Reactive Streams 规范实现,它作为Spring Framework 5的重要组成部分,是WebFlux采用的默认反应式框架。在反应式编程模型中,有一个发布者和一个订阅者。发布者发布订阅服务器异步订阅的数据流。而Flux和Mono就是发布者的实现。Java中异步的传统实现方式是通过异步回调函数和Future,Reactor反应库旨在解决JVM上“经典”异步方法的缺点,同时还拥有如下特点:

  1. 可组合性和可读性,完美规避了Callback Hell
  2. 以流的形式进行数据处理时,为流中每个节点提供了丰富的操作符
  3. 在Subscribe之前,不会有任何事情发生
  4. 支持背压,消费者可以向生产者发出信号表明排放率过高
  5. 支持两种反应序列:hot和cold

发布-订阅模式是借鉴迭代器和观察者模式而设计的
特点:在发布者被订阅之前什么都不会发生。
当调用subscribe方法后,一个发布-订阅的机制形成,只有当对象被从数据源(DB、Remote Call)中取出并放入内存后,JVM才会占用线程资源,将消息发送给订阅者;从阻塞等待转变为了被动接收,因此节省了资源。发布者会在新的可用值出现时通知订阅者,这一推送特性是反应性的关键。此外,应用于推送值的操作以声明方式而不是命令方式表示:开发人员只要声明计算的逻辑,而不是描述其确切的控制流。
除了推送值之外,错误处理和完成方面也以明确定义的方式进行了介绍。发布者可以将新值推送到订阅服务器(通过调用onNext),但也可以发出错误信号(通过调用onError)或完成信号(通过调用onComplete)。错误和完成都会终止序列。这可以总结如下:
onNext x 0…N [onError | onComplete]
Reactor将响应的数据经过一步步包装和转换成不同类型的Mono和Flux,最终返回给客户端。其中包括了数据流式处理和背压(back pressure)等技术。
关于Reactor和Reactor-netty参考:https://projectreactor.io/
https://projectreactor.io/docs/core/release/reference/index.html#getting-started
https://projectreactor.io/docs/netty/release/reference/index.html#getting-started
博文:https://www.jianshu.com/p/df395eb28f69
http://blog.yannxia.top/2018/06/26/java/spring/projectreactor/
Reactor已经和Spring生态形成整合了(https://spring.io/reactive)。
Reactor通过异步的API(比如NoSql数据库异步驱动,Netty的NIO等)调用得到源数据流从而发挥异步非阻塞的应用实现。以下是Spring WebFlux的依赖关系图。

基于Netty的 NIO相关内容(了解)

Java NIO 由以下几个核心部分组成:
• Channels
• Buffers
• Selectors
Channel 和 Buffer
基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点像流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:

Channel和Buffer有好几种类型。下面是JAVA NIO中的一些主要Channel的实现:
• FileChannel
• DatagramChannel
• SocketChannel
• ServerSocketChannel
这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。
Java NIO里关键的Buffer覆盖了通过IO发送的基本数据类型:byte, short, int, long, float, double 和 char。
Selector
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
这是在一个单线程中使用一个Selector处理3个Channel的图示:

要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
为什么要使用开源框架?
JDK原始NIO 的缺点:

  1. NIO的类库和API相对复杂,比如Buffer的使用
  2. Selector编写复杂,如果对某个事件注册后,业务代码过于耦合
  3. 需要了解很多多线程的知识,熟悉网络编程
  4. 面对断连重连、保丢失、粘包等,处理复杂
  5. NIO存在BUG,根据网上言论说是selector空轮训导致CPU飙升,具体有兴趣的可以看看JDK的官网
    Netty介绍
    按照定义来说,Netty是一个异步、事件驱动的用来做高性能、高可靠性的网络应用框架。主要的优点有:
  6. 框架设计优雅,底层模型随意切换适应不同的网络协议要求
  7. 提供很多标准的协议、安全、编码解码的支持
  8. 解决了很多NIO不易用的问题
  9. 社区更为活跃,在很多开源框架中使用,如Dubbo、RocketMQ、Spark等

Spring 社区为了解决Spring MVC的阻塞模型在高并发场景下的性能瓶颈的问题,推出了Spring WebFlux,WebFlux底层实现是久经考验的netty非阻塞IO通信框架,它结合了Reactor数据流编程框架,使用发布和订阅模式。该框架的请求处理与线程交互关系图如下:

bossGroup用于Accetpt连接建立事件并分发请求, workerGroup用于处理I/O读写事件。webClient处理单个HTTP请求的响应时长并不比RestTemplate更快,但是它处理并发的能力更强。 Spring项目因此可以更有效地利用资源,避免为每个请求单独分配一个线程,产生I/O阻塞。所以响应式非阻塞IO模型的核心意义在于:提高了单位时间内有限资源下的服务请求的并发处理能力,而不是缩短了单个服务请求的响应时长。

举报

相关推荐

0 条评论