3 回答

TA貢獻(xiàn)1804條經(jīng)驗(yàn) 獲得超8個(gè)贊
由于存在很多誤解,所以在這里我要澄清一些事情。
Spring 已經(jīng)正式聲明,如果可以的話,如果你想盡可能地證明未來(lái),請(qǐng)使用RestTemplate
它。maintenence mode
WebClient
如RestTemplate API中所述
注意:從 5.0 開始,這個(gè)類處于維護(hù)模式,只有少量的更改請(qǐng)求和錯(cuò)誤被接受。請(qǐng)考慮使用
org.springframework.web.reactive.client.WebClient
具有更現(xiàn)代 API 并支持同步、異步和流式傳輸方案的 。
非反應(yīng)性應(yīng)用
如果您的應(yīng)用程序是非反應(yīng)性應(yīng)用程序(不向調(diào)用客戶端返回通量或單聲道),您必須做的是block()
在需要該值時(shí)使用。您當(dāng)然可以在您的應(yīng)用程序內(nèi)部使用Mono
or?Flux
,但最后您必須調(diào)用block()
以獲取返回給調(diào)用客戶端所需的具體值。
非反應(yīng)性應(yīng)用程序使用例如作為底層服務(wù)器實(shí)現(xiàn),它遵循 servlet 規(guī)范,因此它將為每個(gè)請(qǐng)求分配 1 個(gè)線程,因此您將無(wú)法獲得反應(yīng)性應(yīng)用程序所獲得的性能提升tomcat
。undertow
響應(yīng)式應(yīng)用
另一方面,如果您有一個(gè)反應(yīng)性應(yīng)用程序,則在任何情況下都不應(yīng)調(diào)用block()
您的應(yīng)用程序。阻塞正是它所說(shuō)的,它會(huì)阻塞一個(gè)線程并阻止該線程執(zhí)行直到它可以繼續(xù),這在反應(yīng)世界中是不好的。
您也不應(yīng)該調(diào)用subscribe
您的應(yīng)用程序,除非您的應(yīng)用程序是響應(yīng)的最終消費(fèi)者。例如,如果您正在調(diào)用一個(gè) api 來(lái)獲取數(shù)據(jù)并寫入您的應(yīng)用程序連接到的數(shù)據(jù)庫(kù)。您的后端應(yīng)用程序是最終消費(fèi)者。如果外部客戶端正在調(diào)用您的后端(例如反應(yīng)、角度應(yīng)用程序、移動(dòng)客戶端等),則外部客戶端是最終消費(fèi)者,并且是訂閱者。不是你。
這里的底層默認(rèn)服務(wù)器實(shí)現(xiàn)是一個(gè)netty
服務(wù)器,它是一個(gè)非 servlet、基于事件的服務(wù)器,不會(huì)為每個(gè)請(qǐng)求分配一個(gè)線程,服務(wù)器本身是線程不可知的,任何可用的線程都將在任何請(qǐng)求期間隨時(shí)處理任何事情。
webflux文檔清楚地指出,servlet 3.1+ 支持的服務(wù)器 tomcat 和 jetty 都可以與 webflux 以及非 serlet 服務(wù)器 netty 和 undertow 一起使用。
我怎么知道我有什么應(yīng)用程序?
Spring 指出,如果您同時(shí)擁有spring-web
和spring-webflux
在類路徑中,則應(yīng)用程序?qū)⒅С?code>spring-web并默認(rèn)啟動(dòng)具有底層 tomcat 服務(wù)器的非反應(yīng)性應(yīng)用程序。
如果需要作為彈簧狀態(tài),可以手動(dòng)覆蓋此行為。
在您的應(yīng)用程序中同時(shí)添加
spring-boot-starter-web
和spring-boot-starter-webflux
模塊會(huì)導(dǎo)致 Spring Boot 自動(dòng)配置 Spring MVC,而不是 WebFlux。選擇此行為是因?yàn)樵S多 Spring 開發(fā)人員將其添加spring-boot-starter-webflux
到他們的 Spring MVC 應(yīng)用程序中以使用反應(yīng)式 WebClient。您仍然可以通過(guò)將所選應(yīng)用程序類型設(shè)置為 來(lái)強(qiáng)制執(zhí)行您的選擇SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)
。
“Spring WebFlux 框架”
那么如何按照題目提供的代碼實(shí)現(xiàn)WebClient呢?
@Retryable(maxAttempts = 4,
? ? ? ?value = java.net.ConnectException.class,
? ? ? ?backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
? ? return webClient.get()
? ? ? ? ? ? .uri(url)
? ? ? ? ? ? .exchange()
? ? ? ? ? ? .flatMap(response -> response.toEntity(String.class));
}
我會(huì)說(shuō)這是最簡(jiǎn)單和最少侵入性的實(shí)現(xiàn)。您當(dāng)然需要構(gòu)建一個(gè)合適的網(wǎng)絡(luò)客戶端,@Bean并將其自動(dòng)連接到它的類中。

TA貢獻(xiàn)1868條經(jīng)驗(yàn) 獲得超4個(gè)贊
第一步是WebClient
使用 baseUrl 構(gòu)建對(duì)象;
WebClient webClient = WebClient.builder() .baseUrl("http://localhost:8080/api") //baseUrl .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build();
然后選擇方法并將路徑與請(qǐng)求變量或正文有效負(fù)載一起附加。
ResponseSpec responseSpec = webClient .get() .uri(uriBuilder -> uriBuilder.path("/findById") //additional path .queryParam("id", id).build()) .retrieve() .onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new CustomRuntimeException("Error")));
等待響應(yīng)的block()
功能bodyToMono
。如果你想要響應(yīng)作為字符串,你可以使用谷歌的 gson 庫(kù)來(lái)轉(zhuǎn)換它。
Object response = responseSpec.bodyToMono(Object.class).block();Gson gson = new Gson();String str = gson.toJson(response);
如果你不想知道 api 調(diào)用的狀態(tài),你可以像下面那樣做。
webClient .post() .uri(uri -> uri.path("/save").build()) .body( BodyInserters.fromObject(payload) ) .exchange().subscribe();

TA貢獻(xiàn)1803條經(jīng)驗(yàn) 獲得超3個(gè)贊
首先要了解的是,如果您需要調(diào)用,.block()不妨堅(jiān)持使用RestTemplate,使用 WebClient 將一無(wú)所獲。
如果你想從使用 WebClient 中獲益,你需要開始以反應(yīng)的方式思考。反應(yīng)過(guò)程實(shí)際上只是一系列步驟,每個(gè)步驟的輸入都是前一步的輸出。當(dāng)收到請(qǐng)求時(shí),您的代碼會(huì)創(chuàng)建步驟序列并立即返回并釋放 http 線程。當(dāng)上一步的輸入可用時(shí),框架然后使用工作線程池來(lái)執(zhí)行每個(gè)步驟。
這樣做的好處是在接受競(jìng)爭(zhēng)請(qǐng)求的能力上獲得了巨大的收益,而代價(jià)很小,而不得不重新考慮您編寫代碼的方式。您的應(yīng)用程序只需要一個(gè)非常小的 http 線程池和另一個(gè)非常小的工作線程池。
當(dāng)您的控制器方法返回Monoor時(shí)Flux,您就做對(duì)了,不需要調(diào)用block().
像這樣的最簡(jiǎn)單的形式:
@GetMapping(value = "endpoint", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
@ResponseStatus(OK)
public Mono<String> controllerMethod() {
final UriComponentsBuilder builder =
UriComponentsBuilder.fromHttpUrl("http://base.url/" + "endpoint")
.queryParam("param1", "value");
return webClient
.get()
.uri(builder.build().encode().toUri())
.accept(APPLICATION_JSON_UTF8)
.retrieve()
.bodyToMono(String.class)
.retry(4)
.doOnError(e -> LOG.error("Boom!", e))
.map(s -> {
// This is your transformation step.
// Map is synchronous so will run in the thread that processed the response.
// Alternatively use flatMap (asynchronous) if the step will be long running.
// For example, if it needs to make a call out to the database to do the transformation.
return s.toLowerCase();
});
}
轉(zhuǎn)向反應(yīng)式思考是一個(gè)相當(dāng)大的范式轉(zhuǎn)變,但值得付出努力。堅(jiān)持住,一旦您能夠在整個(gè)應(yīng)用程序中完全沒(méi)有阻塞代碼,這真的不是那么困難。構(gòu)建步驟并返回它們。然后讓框架管理步驟的執(zhí)行。
如果有任何不清楚的地方,我們很樂(lè)意提供更多指導(dǎo)。
記得玩得開心:)
添加回答
舉報(bào)