关于 Spring 5 的 WebClient 的困惑

2022-04-16 10:40:58 +08:00
 fox0001

公司新项目用上了 JDK 17 + Spring 2.6.6 。发现 Spring 5 新增了 WebClient 类,使用非阻塞 IO 、支持函数式编程等等一大堆优点。但是使用时,很多跟 Apache HttpClient 那一套有太多不同。

想问问各位大佬,有没有关于 WebClient 的最佳实践?

另外,如果要使用 WebClient 封装一个通用的 HttpClient 类,要注意什么?

3617 次点击
所在节点    Java
21 条回复
zartouch
2022-04-16 11:30:02 +08:00
webclient 用的是响应式编程那套设计的, 如果不是吞吐量太大 (而且 IO 占比不高的也不好说,具体数据请自行 google ,既然你问了大概率不需要), 最佳实践就是没有必要别用,还是 spring MVC blocking IO 那套好写好维护。
monkeyWie
2022-04-16 11:34:40 +08:00
如果你没用 spring webflux ,就没必要用 webclient
fox0001
2022-04-16 11:59:22 +08:00
@zartouch #1
@monkeyWie #2
那就是回归到 JDK 自带 Http Client 、Apache HttpClient 和 OKHttp 这几个选项了
yazinnnn
2022-04-16 12:00:25 +08:00
https://projectreactor.io/

https://www.baeldung.com/spring-5-webclient

就是用 reactor 包了一下呗,这有啥最佳实践的.....

配合 webflux 一路 compose/flatmap 下去就得了,如果有数据库事物操作的话不要乱用 recover/resume 操作符

貌似 reactive transaction 和 kotlin 的 suspend 函数集成还在 wip 阶段,感兴趣可以自己看看
fox0001
2022-04-16 12:08:25 +08:00
@yazinnnn #4
就是看了太多文章,有太多困惑。
比如:

1 )有文章说不要用 create 方法创建 WebClient 对象,要用 build 方法。但是官方文档没提及这个。

2 ) exchange 方法过时了,想获取更多响应结果的原始信息,不知道怎么处理。

3 )领导说要撸一个通用的 HttpClient 类,屏蔽各种底层实现。但是 WebClient 的函数式编程太爽了,撸个通用的,还不如直接使用。况且各种 http 服务有各种要求…
zartouch
2022-04-16 12:22:17 +08:00
@fox0001 嗯随便找个顺手的就行。 说实话我觉得你对响应式编程和它的应用场景,基本概念还很模糊,最好先看看这部分知识,看点例子。 除非你处理流程都是非阻塞的,否则单独用 webclient 最后你还得 call block 拿结果,等于是阻塞模型,而且 webclient 底层是 netty 非阻塞的那套,线程设置的很少,你用阻塞来玩,性能反而更差,流量大点甚至导致线程用尽。
chendy
2022-04-16 12:33:12 +08:00
WebClient 是 web flux 的,响应式那套东西,怎么说呢,没必要就别用,写起来还是累
另外既然 spring 了就 RestTemplate 呗,底层适配多种 http 库,配合 Spring boot 的 RestTemplateBuilder 用,也是个“通用”的 HttpClient 了
Kyle18Tang
2022-04-16 12:37:16 +08:00
推荐 WebClient ,RestTemplate 已经没有新功能增加了,我记得 Spring 官方文档也有写推荐使用 WebClient ,Spring MVC 项目也可以使用 WebClient 。
Kyle18Tang
2022-04-16 12:39:15 +08:00
As of 5.0 the RestTemplate is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the WebClient which offers a more modern API and supports sync, async, and streaming scenarios.
cheng6563
2022-04-16 12:56:27 +08:00
没事别用 aio ,会变的不幸。我完全理解不了函数式编程有什么爽的。
让我用 aio 我不如直接重新用 go 写。
yazinnnn
2022-04-16 12:59:42 +08:00
配合 mvc 用那就简单了,直接 block()就行

exchangeToMono/exchangeToFlux 方法里可以传一个类型为(ClientResponse)->Mono/Flux 的 lambda,想要啥都在那里面
yazinnnn
2022-04-16 13:03:45 +08:00
go boy 能接受 if err!=null ,却不能接受泛型,看不上函数式不是很正常吗
MakHoCheung
2022-04-16 13:51:17 +08:00
WebClient 可以让你在一次处理请求的时候发超多的异步 RPC 调用,如果用普通的 HTTP Client 的话同时发起的异步 RPC 就受线程池数量限制了,记得最新的 Apache HttpClient 底层也是 NIO 了吧,效果应该跟 WebClient 差不多
fox0001
2022-04-16 15:00:30 +08:00
@Kyle18Tang 8
@yazinnnn #11
@MakHoCheung #13

对于怎样使用 WebClient ,我有太多疑问

1 )是否可以整个系统,所有 restful 客户端使用同一个 webClient 对象?或者是否要改为每个 restful 客户端都各自 builder 一个 webClient ?还是每次请求都 build 一个?

2 ) webClient 的 baseUrl 之类的统一设置,会不会提高性能?

3 )关于 Timeout 配置,build 的时候统一配置的 connectTimeout 、readTimeout 、writeTimeout ,跟 Mono 和 Flux 的 timeout ,两个有什么不同。

4 )写 restful 客户端时,有例子把 webClient 作为私用属性,加上 @Autowired ,让 Spring 初始化后装配进去,也有例子设置 WebClient.Builder 参数。两种方式,哪个更佳?

等等……

所以想问问有没有最佳实践,或者有哪些开源项目的代码可以直接参考?
cnhongwei
2022-04-16 16:43:40 +08:00
byte10
2022-04-16 18:02:35 +08:00
@zartouch 完全正确。
@fox0001 你可以看看这个 https://www.bilibili.com/video/BV1Gq4y1e752 ,这有用到你说的那个响应式编程 httpclient 。#6 楼 说的是非常正确的。如果你的整个项目都是同步编程开发,那么最好不要用响应式编程的工具,不然会很麻烦,除非你很清楚这个使用方式。 这个讲 NIO 的为啥那么强,最好看下这个,https://www.bilibili.com/video/BV1Gq4y1e752
V2Q
2022-04-16 18:08:04 +08:00
最近刚好在用这个,也找了很多,最佳实践是否需要根据自己的场景来呢?可能也有不足的地方,希望懂的大佬提出。

我这边需要对接不同厂家,每个厂家的服务会部署在多个机器上,以下是我的配置

这是 WebClient.Builder 以下配置根据自己情况修改

```java
@Configuration
@EnableWebFlux
@Slf4j
public class WebFluxConfig {

@Bean
public WebClient.Builder getWebClientBuilder() {
//配置固定大小连接池
ConnectionProvider connectionProvider = ConnectionProvider.create("DS-connection", 20);
//设置 ssl 信任客户端
SslContext sslContext = null;
try {
sslContext = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
} catch (SSLException e) {
e.printStackTrace();
}
SslContext finalSslContext = sslContext;
HttpClient httpClient = HttpClient.create(connectionProvider).secure(t -> t.sslContext(finalSslContext))
.tcpConfiguration(tcpClient -> tcpClient.doOnConnect(conn ->
//读超时 30 秒
conn.handler(new ReadTimeoutHandler(30, TimeUnit.SECONDS))
//写超时 30 秒
.handler(new WriteTimeoutHandler(30, TimeUnit.SECONDS))
)
//连接超时 60 秒
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
.option(ChannelOption.TCP_NODELAY, true));

return WebClient.builder().clientConnector(new ReactorClientHttpConnector( httpClient));
}
}
```

不同的厂商 参数 如果是固定的 可以通过读取配置完成

```java
@Component
public class Test {

@Autowired
private WebClient.Builder webClientBuilder;

/**
* @param uri
* @param parameter
* @return
*/
public Mono<HashMap> get(String url, String token, String uri, Object... parameter) throws Exception {
return null;
}

/**
* @param uri
* @return
*/
public Mono<HashMap> get(String url, String token, String uri) throws Exception {
return null;
}

/**
* @param uri
* @param parameter
* @return
*/
public Mono<HashMap> post(String url, String token, String uri, Object parameter) throws Exception {
return null;
}
}
```

异常我是抛出在同一个地方统一处理

最后我觉得 还是需要根据你的实际场景来,我也是第一次用这个,RestTemplate 就向上面说的没有新增加了,推荐使用 webclient ,所以才想尝试一下用的这个,项目主要还是 springmvc ,希望能帮助到你。
lmshl
2022-04-16 18:22:08 +08:00
我用过 > 9 种 AIO Http Client
Scala: http4s / tapir / akka-http / play ws client
Python: aio-http
Kotlin: ktor on CIO
Rust: reqwest
Javascript: axios / fetch

说实话使用体验非常流畅,Spring 新版的 WebClient 因为还是受到 Java 语言不灵活的限制,使用体验大概率是要比以上 9 种都要差一些的。

Reactive Stream 的使用体验就是,>100 TPS 的服务才占用 0.2 个 CPU 核心,以至于我开发的服务在公司里没什么存在感。而且错误处理和理论模型也比 Go 要强好多倍。

既然 Spring 新加了这功能,我觉得你不妨体验一下,至于封装嘛,建议抄一下 Axios 之类的设计,让同事们更好上手。不要固步自封,不然升级了和没升级有什么区别呢。
git00ll
2022-04-16 22:00:18 +08:00
除非你的项目中 http 占用大量资源,否则不如用老牌 apache httpclient 想要 nio 可以用 apache async client 。不推荐用,
git00ll
2022-04-16 22:07:08 +08:00
毕竟大多数项目的耗时大头都在数据库层面,使用堵塞 io 只是比 nio 多占几十个个线程,还不至于影响到问题的核心。
我曾将公司项目从 apache httpclient 改为 webclient ,从压测上看对 tps 提升十分有限。因为 tps 上升之后下游服务瓶颈在数据库,导致堵塞本服务,所以本服务是否用 nio 都不会对 tps 带来本质提升。

但却带来挺多坏处,比如偶尔发生的 io 异常,连接中断之类的。使用 apache httpclient 后再也没有遇到这种情况。

还有就是堵塞式的天生是线程隔离的,输出日志携带 mdc 查日志要方便很多。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/847291

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX