用Spring Boot 3.2虛擬線程搭建靜態文件服務器有多快?

Spring Boot 3.2 於 2023 年 11 月大張旗鼓地發佈,標誌着 Java 開發領域的一個關鍵時刻。這一突破性的版本引入了一系列革命性的功能,包括:

  • 虛擬線程:利用 Project Loom 的虛擬線程釋放可擴展性,從而減少資源消耗並增強併發性。
  • Native Image支持:通過Native Image編譯製作速度極快的應用程序,減少啓動時間並優化資源利用率。
  • JVM 檢查點:利用 CRaC 項目的 JVM 檢查點機制實現應用程序的快速重啓,無需冗長的重新初始化。
  • RestClient:採用新的 RestClient 接口的功能方法,簡化 HTTP 交互並簡化代碼。
  • Spring for Apache Pulsar:利用 Apache Pulsar 的強大功能實現強大的消息傳遞功能,無縫集成到您的 Spring Boot 應用程序中。

其中,虛擬線程是最近 Java 版本中引入的最具變革性的特性之一。正如官方文件所述:虛擬線程是輕量級線程,可減少編寫、維護和調試高吞吐量併發應用程序的工作量。線程是可以調度的最小處理單元。它與其他此類單位同時運行,並且在很大程度上獨立於其他此類單元運行。它是 java.lang.Thread 的一個實例。有兩種線程:平臺線程和虛擬線程。平臺線程是作爲操作系統 (OS) 線程的瘦包裝器實現的。平臺線程在其底層操作系統線程上運行 Java 代碼,平臺線程在平臺線程的整個生命週期內捕獲其操作系統線程。因此,可用平臺線程數限制爲操作系統線程數。與平臺線程一樣,虛擬線程也是 java.lang.Thread 的實例。但是,虛擬線程不綁定到特定的操作系統線程。虛擬線程仍在操作系統線程上運行代碼。但是,當在虛擬線程中運行的代碼調用阻塞 I/O 操作時,Java 運行時會掛起虛擬線程,直到它可以恢復爲止。與掛起的虛擬線程關聯的操作系統線程現在可以自由地對其他虛擬線程執行操作。虛擬線程適用於運行大部分時間被阻塞的任務,通常等待 I/O 操作完成。但是,它們不適用於長時間運行的 CPU 密集型操作。

雖然人們普遍認爲虛擬線程在 I/O 密集型方案中表現出色,但它們在 CPU 密集型任務中的性能仍然是一個問號。本系列文章深入探討了虛擬線程在各種用例中的潛在優勢,從基本的“hello world”到靜態文件服務(I/O 密集型)、QR 碼生成(CPU 密集型)和多部分/表單數據處理(混合工作負載)等實際應用。

在本系列的開頭文章中,我們已經瞭解了虛擬線程與物理線程相比在最簡單(且不切實際)的 hello world 情況下的性能。物理線程和虛擬線程之間幾乎沒有任何性能或資源使用差異。在本文中,我們將更加“實用”,並針對靜態文件服務器情況進行比較。這絕對是一個常見且“真實世界”的案例。讓我們看看這次我們發現了什麼。

如果大家正在做Spring Boot 2.3升級Spring 3.2,這裏順手給大家推薦Spring Boot 2.x 到 3.2 的升級指南

測試環境

所有測試均在配備 16G RAM、8 個物理內核和 4 個效率內核的 MacBook Pro M2 上執行。測試工具是 Bombardier,它是更快的 HTTP 負載測試器之一(用 Go 編寫)。

軟件版本爲:

  • Java v21.0.1
  • Spring Boot 3.2.1

程序配置

除了主 Java 類之外,不需要編寫任何 Java 文件,靜態文件服務器只能通過配置就能發揮作用。

application.properties文件如下:

server.port=3000
spring.mvc.static-path-pattern=/static/**
spring.web.resources.static-locations=file:/Users/mayankc/Work/source/perfComparisons/static/

使用虛擬線程時,我們將通過添加以下行來啓用它們:

spring.threads.virtual.enabled=true

pom.xml內容:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.1</version>
    <relativePath/>
 </parent>
 <groupId>com.example</groupId>
 <artifactId>demo</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>demo</name>
 <description>Demo project for Spring Boot</description>
 <properties>
   <java.version>21</java.version>
 </properties>
 <dependencies>
   <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
 </dependencies>

測試數據

大小完全相同但數據不同的 100K 文件被放置在靜態資源目錄中。每個文件大小正好是 102400 字節。

文件的命名範圍爲 1 到 100000。

使用 Bombardier 的修改版本,爲每次運行生成隨機請求 URL: http://localhost:3000/static/<file-name>

應用場景

爲了確保結果一致,每個測試在開始數據收集之前都會經歷 5K 請求預熱階段。

然後,在不同範圍的併發連接級別(50、100 和 300)中仔細記錄測量結果,每個級別都承受 500 萬個請求工作負載。

結果評估

除了簡單地跟蹤原始速度之外,我們還將採用詳細的指標框架來捕獲延遲分佈(最小值、百分位數、最大值)和吞吐量(每秒請求數)。

CPU 和內存的資源使用情況監控將補充此分析,從而提供不同工作負載下系統性能的全面瞭解。

測試結果

結果以圖表形式呈現如下:

總結

對靜態文件服務的分析表明,物理線程在性能和資源效率方面略勝一籌(與我們的預期相反)。

不過,這種受 I/O 限制的場景可能並不是充分發揮虛擬線程潛力的理想場所。涉及數據庫交互的任務可能會顯示出更多令人信服的優勢。也許負載不足以讓虛擬線程發揮出最大的作用。爲了找出答案,我們將在接下來的文章中介紹 URL短鏈(數據庫驅動)、二維碼生成(CPU受限)和混合工作負載場景(如表單數據處理),旨在揭示虛擬線程真正出類拔萃的案例。

歡迎關注我的公衆號:程序猿DD。第一時間瞭解前沿行業消息、分享深度技術乾貨、獲取優質學習資源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章