Spring WebFlux簡(jiǎn)介使用場(chǎng)景及完整使用示例
Spring WebFlux 是 Spring Framework 5+ 引入的非阻塞、響應(yīng)式 Web 框架,旨在充分利用現(xiàn)代多核處理器和異步 I/O 模型(如 Netty、Undertow、Servlet 3.1+ 容器),處理海量并發(fā)連接,特別適合低延遲、高吞吐量的應(yīng)用場(chǎng)景。它是 Spring MVC 的補(bǔ)充,為響應(yīng)式編程模型提供了完整的支持。
核心功能:
非阻塞 & 響應(yīng)式核心:
- 基于 Project Reactor(Reactive Streams 規(guī)范的實(shí)現(xiàn)),使用
Flux(0…N 個(gè)元素) 和Mono(0…1 個(gè)元素) 作為核心響應(yīng)式類型。 - 整個(gè)請(qǐng)求處理鏈(Controller -> Service -> DAO)在事件循環(huán)線程上運(yùn)行,避免了為每個(gè)請(qǐng)求分配一個(gè)線程的傳統(tǒng)阻塞模型,極大減少線程上下文切換開(kāi)銷和內(nèi)存消耗。
- 通過(guò)異步 I/O 操作(如網(wǎng)絡(luò)調(diào)用、數(shù)據(jù)庫(kù)訪問(wèn))釋放線程,讓線程去處理其他請(qǐng)求,提高資源利用率。
- 基于 Project Reactor(Reactive Streams 規(guī)范的實(shí)現(xiàn)),使用
函數(shù)式編程模型:
- 基于注解的控制器 (
@Controller,@RestController): 與 Spring MVC 風(fēng)格類似,但方法返回類型是Mono/Flux/Mono<Void>等響應(yīng)式類型。 - 函數(shù)式端點(diǎn) (Router Functions): 提供輕量級(jí)、函數(shù)式的編程模型,通過(guò)
RouterFunction和HandlerFunction定義路由和處理邏輯,更顯式地控制請(qǐng)求處理流程,減少注解開(kāi)銷。
- 基于注解的控制器 (
響應(yīng)式 HTTP 客戶端 (
WebClient):- 非阻塞、響應(yīng)式的 HTTP 客戶端,替代傳統(tǒng)的阻塞式
RestTemplate。 - 提供流暢的 API 進(jìn)行聲明式調(diào)用,支持異步、流式數(shù)據(jù)處理和背壓。
- 非阻塞、響應(yīng)式的 HTTP 客戶端,替代傳統(tǒng)的阻塞式
響應(yīng)式 WebSocket 支持:
- 提供處理 WebSocket 連接的 API,支持雙向、全雙工的流式通信,返回和消費(fèi)
Flux/Mono。
- 提供處理 WebSocket 連接的 API,支持雙向、全雙工的流式通信,返回和消費(fèi)
響應(yīng)式 Server-Sent Events (SSE) 支持:
- 輕松實(shí)現(xiàn)服務(wù)器向客戶端單向推送事件流的功能,控制器方法可以直接返回
Flux<ServerSentEvent>。
- 輕松實(shí)現(xiàn)服務(wù)器向客戶端單向推送事件流的功能,控制器方法可以直接返回
與響應(yīng)式數(shù)據(jù)存儲(chǔ)集成:
- 與響應(yīng)式數(shù)據(jù)庫(kù)驅(qū)動(dòng)(如 R2DBC for SQL, Reactive MongoDB, Reactive Cassandra, Reactive Redis)和響應(yīng)式消息隊(duì)列(如 Reactor Kafka, RabbitMQ Reactor)無(wú)縫集成。
背壓 (Backpressure) 支持:
- 響應(yīng)式流的核心機(jī)制,允許消費(fèi)者控制生產(chǎn)者的速度,防止下游被上游過(guò)快的數(shù)據(jù)淹沒(méi),確保系統(tǒng)穩(wěn)定。
主要使用場(chǎng)景:
- 高并發(fā) & 高吞吐量應(yīng)用: 需要處理成千上萬(wàn)甚至百萬(wàn)級(jí)并發(fā)連接的場(chǎng)景(如實(shí)時(shí)聊天、游戲服務(wù)器、金融交易平臺(tái)、物聯(lián)網(wǎng)數(shù)據(jù)采集網(wǎng)關(guān))。
- 低延遲要求: 需要極快響應(yīng)時(shí)間的應(yīng)用,非阻塞模型減少了線程阻塞帶來(lái)的延遲。
- 流式數(shù)據(jù)處理: 處理持續(xù)不斷的實(shí)時(shí)數(shù)據(jù)流(如傳感器數(shù)據(jù)、日志流、股票行情、視頻流處理)。
- 異步 & 非阻塞 I/O 密集型服務(wù): 服務(wù)的主要瓶頸在于等待外部資源響應(yīng)(如微服務(wù)間調(diào)用、數(shù)據(jù)庫(kù)查詢、外部 API 調(diào)用)。
- 資源受限環(huán)境: 需要節(jié)省內(nèi)存和 CPU 資源的場(chǎng)景(如云原生環(huán)境、容器化部署),因?yàn)?WebFlux 通常比線程阻塞模型使用更少的線程。
- 構(gòu)建響應(yīng)式系統(tǒng): 作為構(gòu)建端到端響應(yīng)式微服務(wù)架構(gòu)的 Web 層組件。
何時(shí) 不 首選 WebFlux?
- 簡(jiǎn)單 CRUD 應(yīng)用: 并發(fā)量不高,使用熟悉的 Spring MVC + 阻塞式數(shù)據(jù)庫(kù)驅(qū)動(dòng)開(kāi)發(fā)更快、更簡(jiǎn)單。
- 強(qiáng)事務(wù)性、復(fù)雜阻塞邏輯: 如果業(yè)務(wù)邏輯本身是重度計(jì)算密集型(CPU Bound)或者必須依賴阻塞式庫(kù)(如 JDBC)且難以替換,強(qiáng)行用 WebFlux 可能收益不大甚至引入復(fù)雜度。
- 團(tuán)隊(duì)缺乏響應(yīng)式經(jīng)驗(yàn): 響應(yīng)式編程范式(如函數(shù)式、聲明式、異步流處理)學(xué)習(xí)曲線較陡峭,調(diào)試也更復(fù)雜。
完整使用示例 (基于注解的 Controller + Reactive MongoDB + WebClient)
1. 依賴 (Maven - pom.xml):
<dependencies>
<!-- Spring Boot Starter WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Spring Data Reactive MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<!-- Project Reactor (通常由 starter 帶進(jìn)來(lái)) -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
</dependencies>2. 實(shí)體類 (User.java):
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "users") // MongoDB 集合映射
public class User {
@Id
private String id;
private String name;
private String email;
}3. 響應(yīng)式 Repository (UserRepository.java):
public interface UserRepository extends ReactiveMongoRepository<User, String> {
Flux<User> findByName(String name); // 響應(yīng)式查詢方法
}
4. Service 層 (UserService.java):
@Service
public class UserService {
private final UserRepository userRepository;
private final WebClient webClient; // 響應(yīng)式 HTTP 客戶端
public UserService(UserRepository userRepository, WebClient.Builder webClientBuilder) {
this.userRepository = userRepository;
this.webClient = webClientBuilder.baseUrl("http://some-external-api.com").build();
}
public Flux<User> getAllUsers() {
return userRepository.findAll();
}
public Mono<User> getUserById(String id) {
return userRepository.findById(id);
}
public Mono<User> createUser(User user) {
return userRepository.save(user);
}
public Mono<User> updateUser(String id, User user) {
return userRepository.findById(id)
.flatMap(existingUser -> {
existingUser.setName(user.getName());
existingUser.setEmail(user.getEmail());
return userRepository.save(existingUser);
});
}
public Mono<Void> deleteUser(String id) {
return userRepository.deleteById(id);
}
// 示例:使用 WebClient 調(diào)用外部API并處理響應(yīng)
public Mono<String> fetchDataFromExternalApi() {
return webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class)
.onErrorResume(e -> Mono.just("Fallback Data")); // 錯(cuò)誤處理示例
}
}5. Controller 層 (UserController.java):
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// 獲取所有用戶 (返回 Flux<User>)
@GetMapping
public Flux<User> getAllUsers() {
return userService.getAllUsers();
}
// 根據(jù)ID獲取用戶 (返回 Mono<User>)
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> getUserById(@PathVariable String id) {
return userService.getUserById(id)
.map(user -> ResponseEntity.ok(user)) // 找到用戶,返回200 OK
.defaultIfEmpty(ResponseEntity.notFound().build()); // 用戶不存在,返回404
}
// 創(chuàng)建用戶 (接受 Mono<User> 請(qǐng)求體,返回 Mono<User>)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<User> createUser(@RequestBody Mono<User> userMono) {
return userMono.flatMap(userService::createUser);
}
// 更新用戶
@PutMapping("/{id}")
public Mono<ResponseEntity<User>> updateUser(@PathVariable String id, @RequestBody Mono<User> userMono) {
return userMono.flatMap(user -> userService.updateUser(id, user))
.map(updatedUser -> ResponseEntity.ok(updatedUser))
.defaultIfEmpty(ResponseEntity.notFound().build());
}
// 刪除用戶 (返回 Mono<Void> 表示操作完成)
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public Mono<Void> deleteUser(@PathVariable String id) {
return userService.deleteUser(id);
}
// 示例:Server-Sent Events (SSE) 端點(diǎn) - 模擬持續(xù)發(fā)送用戶列表更新
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamUserUpdates() {
return Flux.interval(Duration.ofSeconds(1)) // 每秒觸發(fā)一次
.flatMap(tick -> userService.getAllUsers().take(5)); // 每次發(fā)送前5個(gè)用戶(模擬更新)
}
// 示例:調(diào)用外部API
@GetMapping("/external-data")
public Mono<String> getExternalData() {
return userService.fetchDataFromExternalApi();
}
}6. 配置 (application.properties or application.yml):
# MongoDB 連接配置 spring.data.mongodb.uri=mongodb://localhost:27017/mydatabase # (可選) 設(shè)置 Netty 作為內(nèi)嵌服務(wù)器 (默認(rèn)通常是 Netty) server.port=8080
示例說(shuō)明:
- 依賴: 引入了 WebFlux、響應(yīng)式 MongoDB 和 Reactor 的核心依賴。
- 實(shí)體 & Repository: 定義了
User實(shí)體和響應(yīng)式的 Spring Data MongoDB Repository 接口。 - Service:
- 注入
UserRepository進(jìn)行響應(yīng)式數(shù)據(jù)庫(kù)操作 (findAll,findById,save,deleteById)。 - 注入
WebClient(通過(guò)WebClient.Builder構(gòu)造) 用于進(jìn)行非阻塞的 HTTP 調(diào)用。fetchDataFromExternalApi方法展示了基本用法和錯(cuò)誤處理 (onErrorResume)。 - 所有 Service 方法返回
Flux或Mono。
- 注入
- Controller:
- 使用
@RestController和@RequestMapping定義 REST 端點(diǎn)。 - 方法參數(shù)可以接受
@RequestBody Mono<User>表示請(qǐng)求體是異步到達(dá)的。 - 方法返回類型都是
Flux或Mono。 getUserById和updateUser展示了如何處理可能為空的結(jié)果,返回不同的ResponseEntity狀態(tài)碼。deleteUser返回Mono<Void>并設(shè)置@ResponseStatus(HttpStatus.NO_CONTENT)。streamUserUpdates方法:- 設(shè)置
produces = MediaType.TEXT_EVENT_STREAM_VALUE表明這是一個(gè) SSE 端點(diǎn)。 - 使用
Flux.interval每秒生成一個(gè)信號(hào)。 - 每次信號(hào)觸發(fā)時(shí),調(diào)用
userService.getAllUsers().take(5)獲?。M的)最新用戶數(shù)據(jù)(這里簡(jiǎn)單取了前5個(gè))并發(fā)送給客戶端??蛻舳藭?huì)持續(xù)接收到事件流。
- 設(shè)置
getExternalData方法展示了如何在 Controller 中調(diào)用 Service 層的 WebClient 功能。
- 使用
- 運(yùn)行: 啟動(dòng) Spring Boot 應(yīng)用。應(yīng)用默認(rèn)會(huì)使用 Netty 作為內(nèi)嵌服務(wù)器??梢允褂?
curl, Postman 或?yàn)g覽器(對(duì)于/api/users/stream)測(cè)試各個(gè)端點(diǎn)。
關(guān)鍵注意事項(xiàng):
- 非阻塞貫穿始終: 從 Controller 到 Service 到 Repository (或 WebClient) 的整個(gè)調(diào)用鏈必須是響應(yīng)式和非阻塞的。如果在其中任何一環(huán)使用了阻塞操作(如傳統(tǒng)的 JDBC 調(diào)用、
Thread.sleep()、同步 IO),會(huì)破壞響應(yīng)式模型的優(yōu)勢(shì),甚至可能導(dǎo)致性能下降或線程饑餓。 - 學(xué)習(xí)曲線: 響應(yīng)式編程范式(
Flux,Mono, 操作符如map,flatMap,filter,zip,onErrorResume等)需要學(xué)習(xí)和適應(yīng)。 - 調(diào)試: 調(diào)試響應(yīng)式流比調(diào)試傳統(tǒng)的命令式代碼更具挑戰(zhàn)性,堆棧跟蹤可能不那么直觀。利用 Reactor 的調(diào)試工具(如
Hooks.onOperatorDebug())和日志記錄很重要。 - 現(xiàn)有庫(kù)兼容性: 確保你使用的所有庫(kù)(數(shù)據(jù)庫(kù)驅(qū)動(dòng)、HTTP 客戶端、消息隊(duì)列客戶端等)都有響應(yīng)式版本或支持非阻塞操作。使用阻塞庫(kù)會(huì)破壞響應(yīng)式鏈。
- 背壓理解: 理解背壓機(jī)制以及如何在你的應(yīng)用中正確處理它是構(gòu)建健壯響應(yīng)式系統(tǒng)的關(guān)鍵。
總結(jié):
Spring WebFlux 是一個(gè)強(qiáng)大的框架,為構(gòu)建高性能、可擴(kuò)展、資源高效的異步和非阻塞 Web 應(yīng)用程序和微服務(wù)提供了現(xiàn)代解決方案。它特別適合處理高并發(fā)、低延遲和流式數(shù)據(jù)場(chǎng)景。在決定采用 WebFlux 時(shí),務(wù)必權(quán)衡其優(yōu)勢(shì)(性能、可伸縮性)與挑戰(zhàn)(學(xué)習(xí)曲線、調(diào)試、庫(kù)兼容性),確保它適合你的具體應(yīng)用需求和團(tuán)隊(duì)技能。上面的完整示例展示了如何使用注解模型結(jié)合響應(yīng)式 MongoDB 和 WebClient 構(gòu)建一個(gè)基本的 CRUD API,并包含 SSE 和外部 API 調(diào)用的示例。
到此這篇關(guān)于Spring WebFlux 功能介紹,使用場(chǎng)景,完整使用示例的文章就介紹到這了,更多相關(guān)Spring WebFlux 使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot登錄攔截配置詳解(實(shí)測(cè)可用)
這篇文章主要介紹了SpringBoot登錄攔截配置詳解(實(shí)測(cè)可用),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
為Java應(yīng)用創(chuàng)建Docker鏡像的3種方式總結(jié)
Docker的使用可以將應(yīng)用程序做成鏡像,這樣可以將鏡像發(fā)布到私有或者公有倉(cāng)庫(kù)中,在其他主機(jī)上也可以pull鏡像,并且運(yùn)行容器,運(yùn)行程,下面這篇文章主要給大家總結(jié)介紹了關(guān)于為Java應(yīng)用創(chuàng)建Docker鏡像的3種方式,需要的朋友可以參考下2023-06-06
SpringBoot中@Configuration和@Bean和@Component相同點(diǎn)詳解
這篇文章主要介紹了SpringBoot中@Configuration和@Bean和@Component相同點(diǎn)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
java.sql.SQLException:?connection?holder?is?null錯(cuò)誤解決辦法
這篇文章主要給大家介紹了關(guān)于java.sql.SQLException:?connection?holder?is?null錯(cuò)誤的解決辦法,這個(gè)錯(cuò)誤通常是由于連接對(duì)象為空或未正確初始化導(dǎo)致的,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-02-02
java內(nèi)存模型jvm虛擬機(jī)簡(jiǎn)要分析
Java 內(nèi)存模型的主要目的是定義程序中各種變量的訪問(wèn)規(guī)則, 關(guān)注在虛擬機(jī)中把變量值存儲(chǔ)到內(nèi)存和從內(nèi)存中取出變量值這樣的底層細(xì)節(jié)2021-09-09
SpringCloud容器化服務(wù)發(fā)現(xiàn)及注冊(cè)實(shí)現(xiàn)方法解析
這篇文章主要介紹了SpringCloud容器化服務(wù)發(fā)現(xiàn)及注冊(cè)實(shí)現(xiàn)方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
SpringBoot+Redis實(shí)現(xiàn)接口防刷的示例代碼
在實(shí)際開(kāi)發(fā)中,會(huì)出現(xiàn)用戶多次點(diǎn)擊發(fā)送請(qǐng)求,本文主要介紹了SpringBoot+Redis實(shí)現(xiàn)接口防刷的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01

