前言


Spring5带来了新的响应式web开发框架WebFlux,同时,也引入了新的HttpClient框架WebClient。WebClient是Spring5中引入的执行 HTTP 请求的非阻塞、反应式客户端。它对同步和异步以及流方案都有很好的支持,WebClient发布后,RestTemplate将在将来版本中弃用,并且不会向前添加主要新功能。

WebClient与RestTemplate比较


WebClient是一个功能完善的Http请求客户端,与RestTemplate相比,WebClient支持以下内容:

  • 非阻塞 I/O。
  • 反应流背压(消费者消费负载过高时主动反馈生产者放慢生产速度的一种机制)。
  • 具有高并发性,硬件资源消耗更少。
  • 流畅的API设计。
  • 同步和异步交互。
  • 流式传输支持

HTTP底层库选择

Spring5的WebClient客户端和WebFlux服务器都依赖于相同的非阻塞编解码器来编码和解码请求和响应内容。默认底层使用Netty,内置支持Jetty反应性HttpClient实现。同时,也可以通过编码的方式实现ClientHttpConnector接口自定义新的底层库;如切换Jetty实现:

WebClient.builder()
.clientConnector(new JettyClientHttpConnector())
.build();

WebClient配置


基础配置

WebClient实例构造器可以设置一些基础的全局的web请求配置信息,比如默认的cookie、header、baseUrl等


WebClient.builder()
.defaultCookie("test","t1")
.defaultUriVariables(ImmutableMap.of("name","kl"))
.defaultHeader("header","kl")
.defaultHeaders(httpHeaders -> {
httpHeaders.add("header1","kl");
httpHeaders.add("header2","kl");
})
.defaultCookies(cookie ->{
cookie.add("cookie1","kl");
cookie.add("cookie2","kl");
})
.baseUrl("http://www.kailing.pub")
.build();
 

底层依赖Netty库配置

通过定制Netty底层库,可以配置SSl安全连接,以及请求超时,读写超时等。这里需要注意一个问题,默认的连接池最大连接500。获取连接超时默认是45000ms,你可以配置成动态的连接池,就可以突破这些默认配置,也可以根据业务自己制定。包括Netty的select线程和工作线程也都可以自己设置。

//配置动态连接池
//ConnectionProvider provider = ConnectionProvider.elastic("elastic pool");
//配置固定大小连接池,如最大连接数、连接获取超时、空闲连接死亡时间等
ConnectionProvider provider = ConnectionProvider.fixed("fixed", 45, 4000, Duration.ofSeconds(6));
HttpClient httpClient = HttpClient.create(provider)
.secure(sslContextSpec -> {
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()
.trustManager(new File("E://server.truststore"));
sslContextSpec.sslContext(sslContextBuilder);
}).tcpConfiguration(tcpClient -> {
//指定Netty的select 和 work线程数量
LoopResources loop = LoopResources.create("kl-event-loop", 1, 4, true);
return tcpClient.doOnConnected(connection -> {
//读写超时设置
connection.addHandlerLast(new ReadTimeoutHandler(10, TimeUnit.SECONDS))
.addHandlerLast(new WriteTimeoutHandler(10));
})
//连接超时设置
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.option(ChannelOption.TCP_NODELAY, true)
.runOn(loop);
});
WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();

编解码配置

针对特定的数据交互格式,可以设置自定义编解码的模式,如下:

ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(configurer -> {
configurer.customCodecs().decoder(new Jackson2JsonDecoder());
configurer.customCodecs().encoder(new Jackson2JsonEncoder());
})
.build();
WebClient.builder()
.exchangeStrategies(strategies)
.build();

get请求示例

uri构造时支持属性占位符,真实参数在入参时排序好就可以。同时可以通过accept设置媒体类型,以及编码。最终的结果值是通过Mono和Flux来接收的,在subscribe方法中订阅返回值。

WebClient client = WebClient.create("http://www.kailing.pub");
Mono<String> result = client.get()
.uri("/article/index/arcid/{id}.html", 256)
.acceptCharset(StandardCharsets.UTF_8)
.accept(MediaType.TEXT_HTML)
.retrieve()
.bodyToMono(String.class);
result.subscribe(System.err::println);

如果需要携带复杂的查询参数,可以通过UriComponentsBuilder构造出uri请求地址,如:

//定义query参数
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("name", "kl");
params.add("age", "19");
//定义url参数
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", 200);
String uri = UriComponentsBuilder.fromUriString("/article/index/arcid/{id}.html")

下载文件时,因为不清楚各种格式文件对应的MIME Type,可以设置accept为MediaType.ALL,然后使用Spring的Resource来接收数据即可,如:

WebClient.create("https://kk-open-public.oss-cn-shanghai.aliyuncs.com/xxx.xlsx")
.get()
.accept(MediaType.ALL)
.retrieve()
.bodyToMono(Resource.class)
.subscribe(resource -> {
try {
File file = new File("E://abcd.xlsx");
FileCopyUtils.copy(StreamUtils.copyToByteArray(resource.getInputStream()), file);
}catch (IOException ex){}
});

post请求示例

post请求示例演示了一个比较复杂的场景,同时包含表单参数和文件流数据。如果是普通post请求,直接通过bodyValue设置对象实例即可。不用FormInserter构造。

WebClient client = WebClient.create("http://www.kailing.pub");
FormInserter formInserter = fromMultipartData("name","kl")
.with("age",19)
.with("map",ImmutableMap.of("xx","xx"))
.with("file",new File("E://xxx.doc"));
Mono<String> result = client.post()
.uri("/article/index/arcid/{id}.html", 256)
.contentType(MediaType.APPLICATION_JSON)
.body(formInserter)
//.bodyValue(ImmutableMap.of("name","kl"))
.retrieve()
.bodyToMono(String.class);
result.subscribe(System.err::println);

同步返回结果

上面演示的都是异步的通过mono的subscribe订阅响应值。当然,如果你想同步阻塞获取结果,也可以通过.block()阻塞当前线程获取返回值。

WebClient client =  WebClient.create("http://www.kailing.pub");
String result = client .get()
.uri("/article/index/arcid/{id}.html", 256)
.retrieve()
.bodyToMono(String.class)
.block();
System.err.println(result);

但是,如果需要进行多个调用,则更高效地方式是避免单独阻塞每个响应,而是等待组合结果,如:

WebClient client =  WebClient.create("http://www.kailing.pub");
Mono<String> result1Mono = client .get()
.uri("/article/index/arcid/{id}.html", 255)
.retrieve()
.bodyToMono(String.class);
Mono<String> result2Mono = client .get()
.uri("/article/index/arcid/{id}.html", 254)
.retrieve()
.bodyToMono(String.class);
Map<String,String> map = Mono.zip(result1Mono, result2Mono, (result1, result2) -> {
Map<String, String> arrayList = new HashMap<>();
arrayList.put("result1", result1);
arrayList.put("result2", result2);
return arrayList;
}).block();
System.err.println(map.toString());

Filter过滤器

可以通过设置filter拦截器,统一修改拦截请求,比如认证的场景,如下示例,filter注册单个拦截器,filters可以注册多个拦截器,basicAuthentication是系统内置的用于basicAuth的拦截器,limitResponseSize是系统内置用于限制响值byte大小的拦截器

WebClient.builder()
.baseUrl("http://www.kailing.pub")
.filter((request, next) -> {
ClientRequest filtered = ClientRequest.from(request)
.header("foo", "bar")
.build();
return next.exchange(filtered);
})
.filters(filters ->{
filters.add(ExchangeFilterFunctions.basicAuthentication("username","password"));
filters.add(ExchangeFilterFunctions.limitResponseSize(800));
})
.build().get()
.uri("/article/index/arcid/{id}.html", 254)
.retrieve()
.bodyToMono(String.class)
.subscribe(System.err::println);

websocket支持

WebClient不支持websocket请求,请求websocket接口时需要使用WebSocketClient,如:

WebSocketClient client = new ReactorNettyWebSocketClient();
URI url = new URI("ws://localhost:8080/path");
client.execute(url, session ->
session.receive()
.doOnNext(System.out::println)
.then()
);

原文链接:https://segmentfault.com/a/1190000021133071

参考文档

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client

https://blog.csdn.net/iteye_13139/article/details/82726588

 

Spring5 of WebClient(转载)的更多相关文章

  1. 转载 Silverlight实用窍门系列:1.Silverlight读取外部XML加载配置---(使用WebClient读取XAP包同目录下的XML文件))

    转载:程兴亮文章,地址;http://www.cnblogs.com/chengxingliang/archive/2011/02/07/1949579.html 使用WebClient读取XAP包同 ...

  2. WebClient用法小结(转载)

    如果只想从特定的URI请求文件,则使用WebClient,它是最简单的.NET类,它只用一两条命令执行基本操作,.NET FRAMEWORK目前支持以http:.https:.ftp:.和 file: ...

  3. 装饰者模式的学习(c#) EF SaveChanges() 报错(转载) C# 四舍五入 保留两位小数(转载) DataGridView样式生成器使用说明 MSSQL如何将查询结果拼接成字符串 快递查询 C# 通过smtp直接发送邮件 C# 带参访问接口,WebClient方式 C# 发送手机短信 文件 日志 写入 与读取

    装饰者模式的学习(c#) 案例转自https://www.cnblogs.com/stonefeng/p/5679638.html //主体基类 using System;using System.C ...

  4. async/await 异步编程(转载)

    转载地址:http://www.cnblogs.com/teroy/p/4015461.html 前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入 ...

  5. C# Windows Phone 8 WP8 开发,将WebClient的DownloadStringCompleted事件改成非同步的awiat方法。

    原文:C# Windows Phone 8 WP8 开发,将WebClient的DownloadStringCompleted事件改成非同步的awiat方法. 一般我们在撰写Windows Phone ...

  6. 第三节:总结.Net下后端的几种请求方式(WebClient、WebRequest、HttpClient)

    一. 前言 前端调用有Form表单提交,ajax提交,ajax一般是用Jquery的简化写法,在这里不再过多介绍: 后端调用大约有这些:WebCient.WebRequest.Httpclient.W ...

  7. 【转载】C#工具类:FTP操作辅助类FTPHelper

    FTP是一个8位的客户端-服务器协议,能操作任何类型的文件而不需要进一步处理,就像MIME或Unicode一样.可以通过C#中的FtpWebRequest类.NetworkCredential类.We ...

  8. 《Spring5官方文档》新功能(4,3)

    <Spring5官方文档>新功能 原文链接 译者:supriseli Spring框架的新功能 这一章主要提供Spring框架新的功能和变更. 升级到新版本的框架可以参考.Spring g ...

  9. 转载--无弹窗APT渗透实验

    转载--无弹窗APT渗透实验 文章作者:亚信安全,转载自 FreeBuf.COM APT攻击方式花样繁多,我研究最近的流行的APT攻击方式,在本地搭建环境模拟一次简单的APT攻击,在模拟攻击过程中发现 ...

随机推荐

  1. MySQL5.5升级至5.7

    一.下载免安装MySQL5.7包 下载页面 下载链接 二.替换mysql的安装目录 解压mysql5.7 tar -zxf mysql-5.7.23-linux-glibc2.12-x86_64.ta ...

  2. pytest学习3-断言

    断言: 一个标准的用例都包含了断言,编写pytest自动化脚本的时候,也需要设置断言 pytest常用断言比较大小与是否相等.是否包含.验证boolean 例子一: 验证是否相等: import py ...

  3. VS2017连接MySQL数据库

    vs默认无法直接连接mysql,需要我们自己配置环境. 1.下载mysql-installer-community-8.0.18.0.msi 下载地址:https://dev.mysql.com/do ...

  4. centos7下使用selenium实现文件上传

    1.pip install SendKeys 2. 利用js去掉元素的隐藏属性,然后输入: 一般控制元素显示或隐藏是用display属性来实现的 style.display = “none”,表示元素 ...

  5. [NOI2015] 品酒大会 - 后缀数组,并查集,STL,启发式合并

    [NOI2015] 品酒大会 Description 对于每一个 \(i \in [0,n)\) 求有多少对后缀满足 LCP 长度 \(\le i\) ,并求满足条件的两个后缀权值乘积的最大值. So ...

  6. Python MonkeyRunner 连接设备总是返回连接成功问题

    device = mr.waitForConnection(1,deviceName) 当使用waitForConnection时,不管设备是否连接,device总是返回一个对象,所以没有办法通过 i ...

  7. webpack 打包CSS 引入图片

    加载css 安装style-loader, css-loader npm install style-loader css-loader --save-dev 配置webpack.config.js文 ...

  8. 一、centos7更改网卡名称eth0

    异步赠书:10月Python畅销书升级 [线路图]人工智能到底学什么?! 程序员10月书讯 每周荐书(京东篇):618取胜之道.质量保障.技术解密) Centos7更改网卡名称Eth0 标签: Cen ...

  9. fpga vga 显示

    VGA(Video Graphics Array)是IBM在1987年随PS/2机一起推出的一种视频传输标准,具有分辨率高.显示速率快.颜色丰富等优点,在彩色显示器领域得到了广泛的应用.不支持热插拔, ...

  10. 2.6.1 XML配置:创建XML文件

    (1) 工程名右击---New--file  --  newfile窗口中:filename中输入testng.xml testng.xml 文件中打开后,切换到source 标签中.进行编辑. 内容 ...