🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

新專案為什麼推薦 WebFlux,而非 SpringMVC?

前言

從早期的 Struts 到統治多年的 Spring MVC,我見證了整個 Java Web 開發框架的演進。今天,我想和大家深入聊聊 Spring 5 帶來的這個“新成員”—— WebFlux。有些小夥伴在工作中可能聽說過它,知道它“性能高”、“異步非阻塞”,但真要上手,心裡卻直打鼓:這和 Spring MVC 到底有啥不同?我的專案真的需要它嗎?今天,我們就從底層原理到實戰程式碼,徹底把 WebFlux 講明白。

最近準備面試的小夥伴,可以看一下這個寶藏網站(Java突擊隊):www.susan.net.cn,裡面:面試八股文、場景設計題、Spring 源碼解讀、8 個專案實戰、工作內推什麼都有

01 為什麼是 WebFlux?

要理解 WebFlux,必須先看清楚它要解決的問題。我們最熟悉的 Spring MVC,其核心是建立在 Servlet API 之上的同步阻塞模型。想像這樣一個場景:你的控制器裡有一個方法,需要調用一個外部接口獲取數據,這個接口響應很慢,可能需要 2 秒。

// 傳統的 Spring MVC 控制器
@RestController
public class TraditionalController {
    @GetMapping("/slow")
    public String slowApi() {
        // 模擬一個耗時 2 秒的遠程調用
        String data = someSlowRemoteService.call(); // 執行緒在這裡被阻塞 2 秒!
        return "Data: " + data;
    }
}

問題出在哪裡? 當請求到達伺服器時,Servlet 容器(如 Tomcat)會從它的執行緒池中分配一個工作執行緒來處理這個請求。在這個執行緒執行 someSlowRemoteService.call() 的整整 2 秒鐘裡,這個執行緒什麼也做不了,只能空轉、等待。它無法去處理其他已經到達的請求。如果同一時間有 1000 個這樣的並發請求,Tomcat 就需要準備至少 1000 個執行緒來應對。每個執行緒都消耗內存(約 1MB 堆棧內存)和 CPU 調度資源。當執行緒數超過物理核心承載能力,大量的時間將浪費在線程上下文切換上,導致響應變慢,最終可能因資源耗盡而崩潰。這就是 “一個請求,一個執行緒”的阻塞模型在 I/O 密集型場景下的天然瓶頸。我們投入了大量資源(執行緒),僅僅是為了“等待”,而不是“計算”。有些小夥伴在工作中,可能已經通過增大執行緒池、服務拆分等方式緩解了這個問題,但這本質上是“用資源換吞吐量”,並非最優解。

02 WebFlux 的核心:異步非阻塞與響應式流

WebFlux 的哲學截然不同。它源於響應式編程範式,核心目標是:用少量、固定的執行緒,處理大量並發請求。如何做到?答案是 事件驅動異步非阻塞 I/O。它不再讓執行緒傻等,而是告訴系統:“我去做點別的,等數據準備好了,你再回調通知我”。

Reactor 與 Mono/Flux

這是理解 WebFlux 的第一道坎。WebFlux 建構在 Project Reactor 響應式庫之上,引入了兩個核心類型:

  • Mono: 代表 0 或 1 個 結果的異步序列。可以把它想像成一個“未來可能到來的單個資料包”的承諾。
  • Flux: 代表 0 到 N 個 結果的異步序列。可以把它想像成一個“資料流”,資料項一個接一個地異步發布出來。

看一個代碼對比,立刻就能明白:

// Spring MVC: 直接返回對象
@GetMapping("/user/{id}")
public User getUser(@PathVariable String id) {
    return userService.findById(id); // 阻塞式,執行緒等待資料庫返回
}

// WebFlux: 返回 Mono,代表一個異步承諾
@GetMapping("/user/{id}")
public Mono<User> getUser(@PathVariable String id) {
    return userService.findByIdReactive(id); // 非阻塞,立即返回 Mono,資料稍後填充
}

在 WebFlux 版本中,getUser 方法幾乎瞬間返回,返回的是一個 Mono<User> 的空殼。當底層非阻塞資料庫驅動真的獲取到數據後,會自動將數據“填充”到這個 Mono 裡,並最終發送給客戶端。在這個過程中,執行緒沒有被掛起,它可以立刻去處理其他請求。

我們可以通過下面這張圖,直觀感受兩種模型處理多個慢請求時的巨大差異:

image.png

背壓(Backpressure):響應式流的精髓

這或許是 WebFlux 最精妙也最容易被忽視的特性。在傳統的拉取模型中,消費者控制節奏。而在響應式流中,數據由生產者主動推送,如果生產者太快,消費者來不及處理怎麼辦?背壓機制允許消費者(如下游服務)主動告知生產者(如上游資料源)“我最多還能處理多少”,生產者據此調整推送速率,避免消費者被壓垮。這為構建健壯的流處理系統提供了基礎保障,是 Reactive Streams 規範的核心。

03 兩種編程模型:註解與函數式

有些小夥伴一聽要學新框架就頭大,生怕過去 Spring MVC 的經驗白費。別擔心,WebFlux 貼心地提供了兩種編程模型,平滑過渡。

1. 註解模型:最熟悉的陌生人

這種方式和 Spring MVC 幾乎一模一樣,學習成本極低。主要區別僅在於返回值和部分參數類型。

@RestController
@RequestMapping("/orders")
public class ReactiveOrderController {

    @Autowired
    private ReactiveOrderService orderService;

    // 返回 Flux,代表多個訂單的流
    @GetMapping
    public Flux<Order> getAllOrders() {
        return orderService.findAll();
    }

    // 返回 Mono
    @GetMapping("/{id}")
    public Mono<Order> getOrderById(@PathVariable String id) {
        return orderService.findById(id);
    }

    // 參數也可以是 Mono
    @PostMapping
    public Mono<Void> createOrder(@RequestBody Mono<Order> orderMono) {
        return orderMono.flatMap(orderService::save).then();
    }
}

可以看到,除了 FluxMono 這些類型,其他註解 @RestController@GetMapping 都是老熟人。這對於現有專案進行部分重構或新專案啟動非常友好。

2. 函數式模型:更靈活輕量的選擇

這是 WebFlux 的另一面,更像是在用 Java 8 的 Lambda 表達式和函數式介面來定義路由和處理邏輯,它不依賴於註解。

@Configuration
public class RouterFunctionConfig {

    @Bean
    public RouterFunction<ServerResponse> routeOrder(ReactiveOrderHandler orderHandler) {
        return RouterFunctions.route()
                .GET("/fn/orders", orderHandler::getAll)
                .GET("/fn/orders/{id}", orderHandler::getById)
                .POST("/fn/orders", orderHandler::create)
                .build();
    }
}

@Component
public class ReactiveOrderHandler {
    public Mono<ServerResponse> getAll(ServerRequest request) {
        Flux<Order> orders = ... // 獲取訂單流
        return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(orders, Order.class);
    }
    // ... 其他處理方法
}

函數式模型將所有路由和處理器暴露為明確的 Bean,聲明清晰,易於測試,且運行時開銷更小,特別適合微服務場景中功能明確、結構簡潔的端點。

最近為了幫助大家找工作,專門建了一些工作內推群,各大城市都有,歡迎各位 HR 和找工作的小夥伴進群交流,群裡目前已經收集了 20 多家大廠的工作內推崗位。加蘇三的微信:li_su223,備註:掘金+所在城市,即可進群。

04 深入核心:WebFlux 如何運轉

理解了表面用法,我們以註解模型為例,深入一層,看看一個請求在 WebFlux 內部是如何流轉的。WebFlux 的核心調度器不再是 Servlet 容器的執行緒池,而是一個名為 DispatcherHandler 的組件,它扮演著類似 Spring MVC 中 DispatcherServlet 的角色。

  1. 請求接收: 以 Netty 為例,I/O 執行緒接收到 HTTP 請求,將其封裝為 ServerWebExchange(一個非阻塞的請求-響應交換對象)。
  2. 尋找處理器DispatcherHandler 調用一組 HandlerMapping,根據請求路徑等信息,找到對應的控制器方法(就是一個 Handler)。
  3. 執行處理DispatcherHandler 再通過 HandlerAdapter 去實際執行這個控制器方法。我們的方法返回一個 MonoFlux
  4. 處理結果HandlerResultHandler 負責處理這個反應式返回類型,將流中的數據序列化(如轉為 JSON),並通過非阻塞 I/O 寫回響應。

整個過程中,所有環節都是非阻塞的。執行緒只在有 CPU 計算任務時才忙碌,一旦遇到 I/O 等待,就會去處理其他任務,從而實現極高的資源利用率。

下面是 WebFlux 核心組件協同處理請求的架構圖:

image.png

05 性能與選擇:並非銀彈

讀到這裡,有些小夥伴可能摩拳擦掌,準備把現有專案全盤遷移到 WebFlux。且慢!技術選型最忌“為了用而用”。WebFlux 和 Spring MVC 不是替代關係,而是互補關係,它們共同擴展了 Spring 生態的能力邊界。

性能真相

  • WebFlux 的優勢在於高並發、低延遲的 I/O 密集型場景。當你的應用有大量外部調用(資料庫、微服務、API)、慢連接或長輪詢(如聊天)時,WebFlux 能用更少的資源提供更穩定的吞吐量。
  • WebFlux 不會讓你的 CPU 密集型計算更快。如果業務邏輯本身就是複雜的計算,沒有太多 I/O 等待,那麼切換到 WebFlux 可能看不到收益,甚至因為響應式鏈的開銷而略有下降。
  • 資源利用率是核心優勢。WebFlux 通過減少執行緒數量,降低了內存消耗和上下文切換開銷,使系統在壓力下的表現更加可預測和穩定。

代價與挑戰

  • 編程範式轉換: 從“指令式”思維切換到“聲明式”、“函數式”的反應式思維是一大挑戰。調試鏈式調用的 Mono/Flux 也比調試普通代碼更困難。
  • 生態兼容性: 你的整個技術棧都需要支持非阻塞。這意味著你常用的阻塞式資料庫驅動(如 JDBC)、Redis 客戶端等可能無法直接使用,必須尋找其反應式版本(如 R2DBC、Lettuce)。這是一條“全棧反應式”的不歸路。
  • 學習曲線: 團隊需要時間學習 Reactor 豐富的操作符(map, flatMap, zip 等)和錯誤處理機制。

如何選擇?

你可以遵循以下的決策流程,來判斷你的專案是否真的需要 WebFlux:

image.png

對於新專案: 如果是微服務網關(Spring Cloud Gateway 就是基於 WebFlux)、即時監控、消息推送等場景,WebFlux 是絕佳選擇。

對於現有專案不要輕易重構! 如果 Spring MVC 運行良好,重構的成本和風險極高。一個更務實的切入點是:先在 Spring MVC 專案中使用 WebClient(WebFlux 提供的非阻塞 HTTP 客戶端)來調用外部慢服務,這能立即為你的應用帶來部分非阻塞的優勢。


06 總結

WebFlux 是 Spring 應對現代高並發、低延遲應用需求交出的一份優秀答卷。它通過異步非阻塞和響應式流的技術,在 I/O 密集型領域展現出巨大優勢。但它不是一個“傻瓜式”的性能提升按鈕,而是一套完整的、有門檻的新編程範式。我們的責任不是追逐最酷的技術,而是為業務場景選擇最合適的技術

在你決定擁抱 WebFlux 之前,不妨先問自己三個問題:

  1. 我的應用瓶頸真的是 I/O 嗎?
  2. 我的團隊和技術棧準備好“全棧反應式”了嗎?
  3. 預期的收益能否覆蓋學習和改造成本?

想清楚這些問題,你的選擇自然會清晰起來。技術世界沒有銀彈,理解原理,權衡利弊,方是長期主義者的生存之道

最後說一句(求關注,別白嫖我)

如果這篇文章對您有所幫助,或者有所啟發的話,幫忙關注一下我的同名公眾號:蘇三說技術,您的支持是我堅持寫作最大的動力。求一鍵三連:點贊、轉發、在看。關注公眾號:【蘇三說技術】,在公眾號中回覆:進大廠,可以免費獲取我最近整理的10萬字的面試寶典,好多小夥伴靠這個寶典拿到了多家大廠的offer。


原文出處:https://juejin.cn/post/7581381365494366262


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝16   💬10   ❤️5
416
🥈
我愛JS
📝2   💬8   ❤️4
93
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付