Spring Boot 異步HTTP客戶端配置使用
1. 添加依賴
如果是 Spring Boot 2.x 或 3.x,WebClient 已包含在 spring-boot-starter-webflux 中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>2. WebClient 配置
可以通過(guò) Bean 全局配置,也可以直接使用 WebClient.create()。
2.1 全局 Bean 配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(WebClient.Builder builder) {
return builder
.baseUrl("https://api.example.com") // 可配置默認(rèn) baseUrl
.build();
}
}2.2 自定義連接池和超時(shí)(高級(jí)配置)
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import java.time.Duration;
@Bean
public WebClient webClient() {
ConnectionProvider provider = ConnectionProvider.builder("custom")
.maxConnections(100)
.pendingAcquireTimeout(Duration.ofSeconds(60))
.build();
HttpClient httpClient = HttpClient.create(provider)
.responseTimeout(Duration.ofSeconds(5));
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}3. 異步調(diào)用示例
WebClient 默認(rèn)就是異步非阻塞的(基于 Reactor 響應(yīng)式流)。
3.1 GET 請(qǐng)求
@Autowired
private WebClient webClient;
public Mono<String> getDataAsync() {
return webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class);
}3.2 POST 請(qǐng)求
public Mono<ResponseDto> postDataAsync(RequestDto request) {
return webClient.post()
.uri("/submit")
.bodyValue(request)
.retrieve()
.bodyToMono(ResponseDto.class);
}3.3 在 Controller 中使用
@RestController
public class DemoController {
@Autowired
private WebClient webClient;
@GetMapping("/call-remote")
public Mono<String> callRemote() {
return webClient.get()
.uri("/remote-api")
.retrieve()
.bodyToMono(String.class);
}
}4. 異步并發(fā)請(qǐng)求
比如同時(shí)請(qǐng)求多個(gè)接口并合并結(jié)果:
Mono<String> call1 = webClient.get().uri("/api1").retrieve().bodyToMono(String.class);
Mono<String> call2 = webClient.get().uri("/api2").retrieve().bodyToMono(String.class);
Mono<Tuple2<String, String>> result = Mono.zip(call1, call2);5. 錯(cuò)誤處理
webClient.get().uri("/api")
.retrieve()
.onStatus(HttpStatus::isError, response ->
response.bodyToMono(String.class)
.flatMap(errorBody -> Mono.error(new RuntimeException(errorBody)))
)
.bodyToMono(String.class);6. 配置超時(shí)和連接池參數(shù)
如前文 2.2,使用 Reactor Netty 的 ConnectionProvider 和 HttpClient 可以靈活設(shè)置連接數(shù)、超時(shí)等。
7. 其他常見(jiàn)配置
- 代理設(shè)置
- SSL 配置
- 請(qǐng)求/響應(yīng)日志
- 攔截器(ExchangeFilterFunction)
例如日志攔截器:
WebClient.builder()
.filter((request, next) -> {
System.out.println("Request: " + request.url());
return next.exchange(request);
})
.build();8. 與 Spring MVC Controller 集成
如果你用的是 Spring MVC Controller,可以用 Mono<T> 或 Flux<T> 作為返回值,Spring Boot 會(huì)自動(dòng)處理響應(yīng)式類型。
9. 自定義請(qǐng)求頭、參數(shù)、Cookie
設(shè)置請(qǐng)求頭/參數(shù):
webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/search")
.queryParam("keyword", "spring")
.build())
.header("Authorization", "Bearer xxx")
.cookie("SESSIONID", "abcdefg")
.retrieve()
.bodyToMono(String.class);10. 全局異常處理與重試機(jī)制
全局異常處理:
可以通過(guò) ExchangeFilterFunction 實(shí)現(xiàn)全局異常攔截與處理:
@Bean
public WebClient webClient() {
return WebClient.builder()
.filter((request, next) -> next.exchange(request)
.flatMap(response -> {
if (response.statusCode().isError()) {
return response.bodyToMono(String.class)
.flatMap(errorBody -> Mono.error(new RuntimeException(errorBody)));
}
return Mono.just(response);
}))
.build();
}重試機(jī)制(reactor-retry):
import reactor.util.retry.Retry;
webClient.get()
.uri("/unstable-api")
.retrieve()
.bodyToMono(String.class)
.retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(2)));11. 文件上傳與下載(異步)
文件上傳(multipart/form-data):
import org.springframework.core.io.FileSystemResource;
FileSystemResource file = new FileSystemResource("/path/to/file.txt");
webClient.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData("file", file))
.retrieve()
.bodyToMono(String.class);文件下載:
webClient.get()
.uri("/download")
.retrieve()
.bodyToFlux(DataBuffer.class)
.map(dataBuffer -> {
// 將 DataBuffer 寫入文件
// 省略具體實(shí)現(xiàn)
return dataBuffer;
});12. 與服務(wù)發(fā)現(xiàn)、負(fù)載均衡集成
如果用 Spring Cloud,可直接注入帶負(fù)載均衡的 WebClient:
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
// 使用
@Autowired
private WebClient.Builder webClientBuilder;
webClientBuilder.build()
.get()
.uri("http://service-name/api")
.retrieve()
.bodyToMono(String.class);這里的 service-name 是注冊(cè)中心(如 Eureka、Nacos)中的服務(wù)名,WebClient 會(huì)自動(dòng)負(fù)載均衡。
13. 與 OAuth2 等安全機(jī)制集成
Spring Security 提供了對(duì) OAuth2 的支持,可以自動(dòng)為 WebClient 請(qǐng)求加上 Token。
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager manager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2 =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(manager);
return WebClient.builder()
.apply(oauth2.oauth2Configuration())
.build();
}14. WebClient 性能調(diào)優(yōu)建議
- 連接池參數(shù)調(diào)整合理設(shè)置最大連接數(shù)、每主機(jī)最大連接數(shù)、連接超時(shí)等
- 響應(yīng)式編程配合線程池對(duì)于非響應(yīng)式 Controller 推薦
@Async或線程池配合 - 避免阻塞操作在響應(yīng)式流中避免
block(),否則會(huì)失去異步性能優(yōu)勢(shì) - 合理的重試和超時(shí)防止雪崩和資源耗盡
15. 常見(jiàn)問(wèn)題解答
Q1:WebClient 如何同步調(diào)用?A:可以用 .block(),但這會(huì)阻塞線程,失去異步優(yōu)勢(shì)。一般只在測(cè)試或特殊場(chǎng)景下使用。
Q2:如何打印 WebClient 的請(qǐng)求和響應(yīng)日志?A:可通過(guò) ExchangeFilterFunction 實(shí)現(xiàn),或配置日志框架:
logging.level.reactor.netty.http.client=DEBUG
Q3:WebClient 與 RestTemplate 區(qū)別?
- RestTemplate 是同步阻塞的
- WebClient 是異步非阻塞的,支持響應(yīng)式流
- 官方推薦新項(xiàng)目?jī)?yōu)先用 WebClient
總結(jié)
- 推薦使用 WebClient 作為 Spring Boot 異步 HTTP 客戶端
- 支持靈活配置連接池、超時(shí)、代理、SSL 等
- 默認(rèn)就是異步非阻塞
- 適合高并發(fā)、微服務(wù)場(chǎng)景
- WebClient 支持豐富的異步 HTTP 場(chǎng)景,配置靈活
- 可與 Spring Cloud、Spring Security 等生態(tài)無(wú)縫集成
- 適合微服務(wù)、網(wǎng)關(guān)、爬蟲(chóng)等高并發(fā)異步場(chǎng)景
到此這篇關(guān)于Spring Boot 異步HTTP客戶端配置使用的文章就介紹到這了,更多相關(guān)Spring Boot 異步HTTP內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java算法導(dǎo)論之FloydWarshall算法實(shí)現(xiàn)代碼
這篇文章主要介紹了算法導(dǎo)論之FloydWarshall算法實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05
Java中ByteArrayInputStream和ByteArrayOutputStream用法詳解
這篇文章主要介紹了Java中ByteArrayInputStream和ByteArrayOutputStream用法詳解,?ByteArrayInputStream?的內(nèi)部額外的定義了一個(gè)計(jì)數(shù)器,它被用來(lái)跟蹤?read()?方法要讀取的下一個(gè)字節(jié)2022-06-06
IDEA下創(chuàng)建SpringBoot+MyBatis+MySql項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)登錄與注冊(cè)功能
這篇文章主要介紹了IDEA下創(chuàng)建SpringBoot+MyBatis+MySql項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)登錄與注冊(cè)功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
java如何將一個(gè)float型數(shù)的整數(shù)部分和小數(shù)分別輸出顯示
這篇文章主要介紹了java如何將一個(gè)float型數(shù)的整數(shù)部分和小數(shù)分別輸出顯示,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Java 帶參數(shù)與帶返回值的方法的定義和調(diào)用
在java中,方法就是用來(lái)完成解決某件事情或?qū)崿F(xiàn)某個(gè)功能的辦法。方法實(shí)現(xiàn)的過(guò)程中,會(huì)包含很多條語(yǔ)句用于完成某些有意義的功能——通常是處理文本,控制輸入或計(jì)算數(shù)值,這篇文章我們來(lái)探究一下帶參數(shù)與帶返回值的方法的定義和調(diào)用2022-04-04
Java基于正則表達(dá)式獲取指定HTML標(biāo)簽指定屬性值的方法
這篇文章主要介紹了Java基于正則表達(dá)式獲取指定HTML標(biāo)簽指定屬性值的方法,涉及java基于正則的HTML元素匹配相關(guān)操作技巧,需要的朋友可以參考下2017-01-01

