前言

正如《Java异步编程实战》一书中所述,Spring5中引入了与Web Servlet技术栈并行存在的,框架层面支持异步处理的Webflux技术栈。

利用Spring WebFlux可以实现服务端异步执行,另外其也提供了WebClient用来执行HTTP请求,用来实现客户端异步调用。

WebClient 提供了Reactor的功能性、流畅的API,支持多种异步逻辑处理的声明式编排,另外它是完全非阻塞的,并且支持流式传输。

WebClient的使用

首先我们创建一个简单的web服务器,用来作为webclient的服务端,概要代码如下:

public class Server {public static void main(String[] arg) throws IOException {// 创建 http 服务器, 绑定本地 8080 端口HttpServer httpServer = HttpServer.create(new InetSocketAddress(8080), 0);// 创建上下文监听, "/" 表示匹配所有 URI 请求httpServer.createContext("/", new HttpHandler() {@Overridepublic void handle(HttpExchange httpExchange) throws IOException {// 响应内容byte[] respContents = "Hello World".getBytes("UTF-8");// 设置响应头httpExchange.getResponseHeaders().add("Content-Type", "text/html; charset=UTF-8");httpExchange.sendResponseHeaders(200, respContents.length);// 模拟服务端执行耗时try {Thread.sleep(10000);} catch (Exception e) {}// 设置响应内容httpExchange.getResponseBody().write(respContents);httpExchange.close();}});httpServer.start();}
}
  • 如上代码我们启动了一个Web服务端,服务端内部实现休眠10s用来模拟服务端执行逻辑,最后http 响应body内写回Hello World作为客户端响应结果。

下面我们来看如何使用Webclient来做客户端:

public class App {private static WebClient client;public static void init() {//创建webclient实例client = WebClient.builder(). build();}public static void main(String[] args) throws KsMerchantApiException, IOException {//1.初始化webclientinit();//2.反应式调用,返回reactor流对象Mono<String> resp = client.method(HttpMethod.GET).uri("http://127.0.0.1:8080").retrieve().bodyToMono(String.class);//3.订阅流对象resp.onErrorMap(throwable -> {System.out.println("onErrorMap:" + throwable.getLocalizedMessage());return throwable;}).subscribe(s -> System.out.println("result:" + Thread.currentThread().getName() + " " + s));//4.调用线程打印System.out.println("main is over");
}
  • 如上代码1我们创建了一个WebClient对象

  • 代码2我们使用client发起了一个get请求,并且使用bodyToMono(String.class)返回反应式流对象。

  • 代码3 我们订阅流对象,并打印响应结果。

  • 代码4打印main执行结束了。

首先运行服务端,然后运行客户端,会发现客户端如下输出:

main is over
result:reactor-http-nio-1 Hello World

下面我们来分析下调用逻辑:

  • 客户端调用线程,执行代码2发起调用后会马上返回一个Mono对象,不会阻塞等待服务端写回响应结果;调用线程继续向下运行执行代码3订阅流结果,该过程不会阻塞调用线程,调用线程马上返回;最后调用线程执行代码2打印日志,最终调用线程执行结束。

  • 服务端结束到请求后,会休眠10s,休眠结束后,把结果写回客户端,客户端IO线程接受到服务端响应结果后,回调代码3设置的订阅回调函数,输出响应结果。

可知客户端调用线程在发起请求时没被阻塞,响应结果回来后也没阻塞,而是使用的IO线程来处理响应结果。可知webclient实现了严格意义上的异步调用。

WebClient的线程模型

  • 如上当调用线程使用webclient发起请求后,内部会先创建一个Mono响应对象,然后切换到IO线程具体发起网络请求。

  • 调用线程获取到Mono对象后,一般会订阅,也就是设置一个Consumer用来具体处理服务端响应结果。

  • 服务端接受请求后,进行处理,最后把结果写回客户端,客户端接受响应后,使用IO线程把结果设置到Mono对象,从而触发设置的Consumer回调函数的执行。

注:WebClient默认内部使用Netty实现http客户端调用,这里IO线程其实是netty的IO线程,而netty客户端的IO线程内是不建议做耗时操作的,因为IO线程是用来轮训注册到select上的channel的数据的,如果阻塞了,那么其他channel的读写请求就会得不到及时处理。所以如果consumer内逻辑比较耗时,建议从IO线程切换到其他线程来做。

那么如何切换那?可以使用publishOn把IO线程切换到自定义线程池进行处理:

resp.publishOn(Schedulers.elastic())//切换到Schedulers.elastic()对应的线程池进行处理.onErrorMap(throwable -> {System.out.println("onErrorMap:" + throwable.getLocalizedMessage());return throwable;}).subscribe(s -> System.out.println("result:" + Thread.currentThread().getName() + " " + s));

Reactor中Schedulers提供了几种内置实现:

  • Schedulers.elastic():线程池中的线程是可以复用的,按需创建与空闲回收,该调度器适用于 I/O 密集型任务。

  • Schedulers.parallel():含有固定个数的线程池,该调度器适用于计算密集型任务。

  • Schedulers.single():单一线程来执行任务

  • Schedulers.immediate():立刻使用调用线程来执行。

  • Schedulers.fromExecutor():使用已有的Executor转换为Scheduler来执行任务。

总结

异步非阻塞是未来,而结合反应式框架Reactor或Rxjava可以实现Reactor风格的异步编程,实现声明式编程模式,可以让我们编写的异步代码,可读性大大提高。

戳下面阅读

????

我的第三本书    我的第二本书    我的第一本书

golang并发教程    ForkJoinPool    K8s网络模型

我的视频号

点亮再看哦????

WebClient-Reactor风格的异步调用相关推荐

  1. Java 异步调用实践

    本文要点: 为什么需要异步调用 CompletableFuture 基本使用 RPC 异步调用 HTTP 异步调用 编排 CompletableFuture 提高吞吐量 为什么异步 BIO 模型 首先 ...

  2. Direct3D Draw函数 异步调用原理解析

    概述 在D3D10中,一个基本的渲染流程可分为以下步骤: 清理帧缓存: 执行若干次的绘制: 通过Device API创建所需Buffer: 通过Map/Unmap填充数据到Buffer中: 将Buff ...

  3. 如何从异步调用返回响应?

    我有一个函数foo ,它发出Ajax请求. 如何返回foo的响应? 我尝试从success回调中返回值,以及将响应分配给函数内部的局部变量并返回该局部变量,但这些方法均未真正返回响应. functio ...

  4. openFeign异步调用问题

    报错信息 java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not su ...

  5. springboot 多线程_SpringBoot异步调用@Async

    一. 什么是异步调用? 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行. 二. 如何实现异步调用 ...

  6. C++多线程:package_task异步调用任何目标执行操作

    文章目录 描述 函数成员及使用 总结 我们上一篇描述关于C++多线程中的异步操作相关库( async和 promise),本节将分享c++标准库中最后一个多线程异步操作库 package_task的学 ...

  7. springboot之异步调用@Async

    引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3. ...

  8. 异步调用WCF的方法需要小心的地方

    直接使用下面的代码,由于client对象占用的资源没有被释放,会导致内存泄露 GetSimServiceReference.GetSimServiceClient client = new GetSi ...

  9. 关于webservice的异步调用简单实例

    于webservice的异步调用简单实例 无论在任何情况下,被调用方的代码无论是被异步调用还是同步调用的情况下,被调用方的代码都是一样的, 下面,我们就以异步调用一个webservice 为例作说明. ...

最新文章

  1. 洛谷P1265 公路修建
  2. 真正完美攻略之星月夜~Seven Tales in Spiral(中国同人文字AVG)
  3. Mocha and Math 运算
  4. Oracle数据库表空间占用过大的解决办法
  5. 渗透测试入门DVWA 教程1:环境搭建
  6. python中;是什么意思_Python
  7. 数字PCR(DPCR)和QPCR行业调研报告 - 市场现状分析与发展前景预测
  8. I00035 完美数(Perfect number)
  9. 安全沙箱冲突:Loader.content:XX 不能访问 XX 可以通过调用 Security.allowDomain 来避免此冲突。...
  10. 【图像增强】基于matlab模糊集图像增强【含Matlab源码 394期】
  11. 辨别尸体死亡时间! [转]
  12. 基于ARM64架构飞腾2000CPU的浪潮CE3000F机器安装银河麒麟系统和Docker
  13. 复制命令(COPY)
  14. GB28181设备接入实现web无插件多屏直播
  15. Linux jar包在screen开机自启
  16. 海德汉仿真软件+海德汉西门子视频教程
  17. 如何获取本机电脑的AD域名称
  18. 技嘉b365dv3主板黑苹果efi_黑苹果--技嘉 z390 gaming X 究极方案
  19. 大蟒蛇python头像_程序员用Python获取了自己以前的QQ历史头像,以前的非主流形象简直不忍直视...
  20. reports builder 自动产生编号

热门文章

  1. DWORD转LPCSTR
  2. Android Google Map 开发步骤 地图展示空白问题
  3. 关于处理SQL特殊字符的基本方法总结
  4. QuintoAndar 如何提高转化率
  5. Exoplayer+Exomedia打造自定义播放器(二)
  6. 【LeetCode15】三数之和
  7. 弄得很乱,但是弄完了,阿里云centos7.3+springboot+dubbo+zookeeper
  8. 如何进行图片无损放大
  9. java response.sendredirect_通过response对象的sendRedirect方法重定向网页
  10. 胡扯自己对“梦幻西游”、“梦想世界”和“逍遥传说”的一点想法