前言


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. STL-vector-set_difference B - 人见人爱A-B

    B - 人见人爱A-B 参加过上个月月赛的同学一定还记得其中的一个最简单的题目,就是{A}+{B},那个题目求的是两个集合的并集,今天我们这个A-B求的是两个集合的差,就是做集合的减法运算.(当然,大 ...

  2. Servlet与idea

    1.idea会为每一个Tomcat部署的项目,独立建一份配置文件. 配置文件所在位置 怎么部署的?使用的第三种部署方式(查看虚拟目录) docBase部署项目存放的路径 项目目录和Tomcat部署目录 ...

  3. 洛谷P1051 谁拿了最多奖学金

    https://www.luogu.org/problem/P1051 #include<bits/stdc++.h> using namespace std; struct node { ...

  4. jQuery的12种选择器

    jQuery的12种选择器  1.#id : 根据给定的ID匹配一个元素 显示(用加粗的代替颜色): 这是第一个p标签 2.* : 匹配所有元素,多用于结合上下文来搜索 显示 : 这是p标签 这是di ...

  5. Python之路Day07

    基础数据类型补充 str s.capitalize() -- 首字母大写 s.title() -- 每个单词首字母大写 s.swapcase() -- 大小写转换 s.center() -- 居中/填 ...

  6. C++ 实例练习-替换原生数组

    C++ 实例练习-替换原生数组 main.cpp #include <stdio.h> #include "intarray.h" int main(int argc, ...

  7. 管理QT的组件

    1.在qt的安装目录找到'%QTROOT%\MaintenanceTool.exe'. 2.点击MaintenanceTool的设置,可以设置默认储存库.临时储存库.用户定义储存库,选择其中的临时储存 ...

  8. vue-element-admin框架快速入门

    年底了,最近公司也不是太忙,感觉今年互联网行业都遇到寒冬,不在是前两年像热的发烫的赛道.这几天完成公司项目系统的优化和升级,目前准备想开发一套前后端分离的系统.       现在java最新最火的技术 ...

  9. ASP.NET + MVC5 入门完整教程七 -—-- MVC基本工具(上)

    https://blog.csdn.net/qq_21419015/article/details/80474956 这里主要介绍三类工具之一的 依赖项注入(DI)容器,其他两类 单元测试框架和模仿工 ...

  10. dfs题型二(迷宫问题)

    取自:<王道论坛计算机考研机试指南>6.5节 例 6.7 Temple of the bone(九度 OJ 1461)时间限制:1 秒 内存限制:32 兆 特殊判题:否题目描述:The d ...