Web應用和Project Loom

工程 | Mark Thomas | 2023年2月27日 | ...

引言

Project Loom 旨在為 JRE 帶來“易用、高吞吐量、輕量級併發”。Project Loom 引入的一項特性是虛擬執行緒。在這篇博文中,我們將透過部署在 Apache Tomcat 上的幾個簡單 Web 應用,探討虛擬執行緒對 Web 應用的意義。

高吞吐量 / 輕量級

第一個實驗比較了使用 Tomcat 標準執行緒池所帶來的開銷與使用基於虛擬執行緒 (Loom) 的執行器所帶來的開銷。本文末尾詳細介紹了使用的測試環境。我們使用平均每秒請求數來考察不同響應大小和請求併發度下的效能。結果如下圖所示。

loom-results-01

結果表明,通常,建立新虛擬執行緒來處理請求的開銷小於從執行緒池獲取平臺執行緒的開銷。

線上程池測試中看到的一個意外結果是,對於較小的響應體來說,更顯著的是,2 個併發使用者導致的平均每秒請求數少於單個使用者。調查發現,額外的延遲發生在任務被傳遞給 Executor 到 Executor 呼叫任務的 run() 方法之間。對於 4 個併發使用者,這種差異減小了;對於 8 個併發使用者,這種差異幾乎消失了。

在高併發水平下,當併發任務數超過可用的處理器核心數時,虛擬執行緒執行器再次表現出更高的效能。這在使用較小響應體的測試中更加明顯。

易用性

第二個實驗比較了使用 Servlet 非同步 I/O 和標準執行緒池所獲得的效能與使用簡單阻塞 I/O 和基於虛擬執行緒的執行器所獲得的效能。虛擬執行緒在這裡的潛在好處是簡單性。阻塞讀寫比等效的 Servlet 非同步讀寫要簡單得多——尤其是在考慮錯誤處理時。

Servlet 非同步 I/O 通常用於訪問響應延遲較大的外部服務。測試 Web 應用在 Service 類中模擬了這一點。與基於虛擬執行緒的執行器一起使用的 Servlet 以阻塞方式訪問服務,而與標準執行緒池一起使用的 Servlet 使用 Servlet 非同步 API 訪問服務。其中不涉及任何網路 I/O,但這不應該影響結果。

最初的測試結果毫不奇怪地顯示,由於時間主要由 5 秒的延遲決定,阻塞方式和非同步方式之間沒有可衡量的差異。為了在沒有延遲影響的情況下探究差異,將延遲減少到零,並執行了一組類似於吞吐量測試的測試。結果顯示在下圖中。

loom-results-02

我們再次看到,虛擬執行緒通常效能更高,這種差異在低併發度下以及當併發度超過測試環境可用的處理器核心數時最為顯著。

分析

基於虛擬執行緒的執行器與 Tomcat 標準執行緒池之間的差異並沒有圖表上看起來那麼鮮明。這些測試旨在檢查每種方法相關的開銷,並且不代表實際應用場景。在實際應用中,測試中顯示的差異與完成一個請求所需的時間相比,可能可以忽略不計。

Tomcat 標準執行緒池和基於虛擬執行緒的執行器之間效能差異的主要驅動因素是向執行緒池佇列新增和移除任務時的爭用。透過最佳化 Tomcat 當前使用的實現,有可能減少標準執行緒池佇列中的爭用,並提高吞吐量。

影響相對效能的次要因素是上下文切換。這可能是第二個實驗中當併發度超過可用處理器核心數時觀察到的效能差異的原因,因為虛擬執行緒的上下文切換開銷小於標準執行緒池中的執行緒。

結論

使用基於虛擬執行緒的執行器是 Tomcat 標準執行緒池的一個可行替代方案。在容器開銷方面,切換到虛擬執行緒執行器帶來的好處是微不足道的。

在 Tomcat 上執行的經典 Spring MVC 等會遇到阻塞的 Web 應用,並且尚未切換到 Servlet 非同步 API、響應式程式設計或其他非同步 API 的,透過切換到基於虛擬執行緒的執行器應該會看到一些可擴充套件性方面的改進。根據 Web 應用的不同,這些改進可能無需更改 Web 應用程式碼即可實現。

已經切換到使用 Servlet 非同步 API、響應式程式設計或其他非同步 API 的 Web 應用,透過切換到基於虛擬執行緒的執行器,不太可能觀察到可衡量的差異(正面或負面)。

從長遠來看,虛擬執行緒的最大好處似乎是應用程式碼更簡單。目前需要使用 Servlet 非同步 API、響應式程式設計或其他非同步 API 的一些用例,將能夠透過使用阻塞 I/O 和虛擬執行緒來實現。需要注意的是,應用通常需要對不同的外部服務進行多次呼叫。這最有效地方法是並行執行,雖然像 Project Reactor 這樣的框架為此提供了第一類支援,但 JRE 中與此等效的解決方案(結構化併發)仍處於孵化階段,並且僅旨在協調多個 future,而不是以最便捷的方式相互宣告或組合它們。

最後,Project Loom 仍處於預覽模式。現在考慮在生產環境中使用虛擬執行緒還為時過早,但現在是時候將 Project Loom 和虛擬執行緒納入您的規劃中,以便在虛擬執行緒在 JRE 中正式可用時做好準備。

測試環境

測試環境包括以下內容

測試是在一臺完全更新的 Ubuntu 22.04.1 LTS 機器上進行的,該機器配備 Intel i7-6950X 處理器和 32 GB 記憶體。

為了最大化測試之間的差異可見性,對預設配置進行了以下更改以最小化公共開銷

  • 在單臺機器上使用迴環介面執行測試以最小化網路開銷
  • 停用訪問日誌,因為在高請求量下它是重要的磁碟 I/O 源
  • 將 maxKeepAliveRequests 設定為 -1 以減少建立和拆除 TCP 連線所花費的時間

測試 Web 應用 也被設計用於最小化公共開銷並突出測試之間的差異。

使用的 server.xml 檔案為

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />

  <Service name="Catalina">

    <Executor
        className="org.apache.catalina.core.LoomExecutor"
        name="loomExecutor"
        />

    <Connector 
        protocol="org.apache.coyote.http11.Http11NioProtocol"
        port="8080"
        maxKeepAliveRequests="-1"
        />

    <Connector
        executor="loomExecutor"
        protocol="org.apache.coyote.http11.Http11NioProtocol"
        port="8081"
        maxKeepAliveRequests="-1"
        />

    <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
      </Host>
    </Engine>
  </Service>
</Server>

使用的 setenv.sh 檔案為

#!/bin/sh
JAVA_OPTS=--enable-preview

訂閱 Spring 時事通訊

保持與 Spring 時事通訊的聯絡

訂閱

搶先一步

VMware 提供培訓和認證,助您快速進步。

瞭解更多

獲取支援

Tanzu Spring 透過一個簡單的訂閱提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視全部