搶先一步
VMware 提供訓練和認證,以加速您的進展。
瞭解更多Project Loom 旨在為 JRE 帶來「易於使用、高吞吐量、輕量級並行處理」。Project Loom 引入的一項功能是虛擬執行緒。在這篇部落格文章中,我們將探討虛擬執行緒對於 Web 應用程式的意義,並使用一些部署在 Apache Tomcat 上的簡單 Web 應用程式進行說明。
第一個實驗是比較使用 Tomcat 標準執行緒池的開銷與使用基於虛擬執行緒 (Loom) 的執行器的開銷。本文末尾詳細介紹了測試環境。針對不同的回應大小和請求並行性,使用平均每秒請求數來檢視效能。結果如下圖所示。
結果顯示,一般而言,建立新的虛擬執行緒來處理請求的開銷,小於從執行緒池中取得平台執行緒的開銷。
在執行緒池測試中看到一個出乎意料的結果是,對於較小的回應本文,2 個並行使用者導致的平均每秒請求數少於單一使用者。調查發現,額外的延遲發生在將任務傳遞給 Executor 與 Executor 呼叫任務的 run() 方法之間。對於 4 個並行使用者,這種差異有所減少,而對於 8 個並行使用者,這種差異幾乎消失。
在高並行層級,當並行任務多於可用的處理器核心時,虛擬執行緒執行器再次顯示出更高的效能。這在使用較小回應本文的測試中更為明顯。
第二個實驗比較了使用 Servlet 非同步 I/O 與標準執行緒池所獲得的效能,以及使用簡單的阻塞 I/O 與基於虛擬執行緒的執行器所獲得的效能。虛擬執行緒在此處的潛在優勢是簡潔性。阻塞式讀取或寫入比等效的 Servlet 非同步讀取或寫入要簡單得多 - 尤其是在考慮錯誤處理時。
Servlet 非同步 I/O 通常用於存取某些外部服務,這些服務的回應存在明顯的延遲。測試 Web 應用程式在 Service 類別中模擬了這一點。與基於虛擬執行緒的執行器一起使用的 Servlet 以阻塞式樣式存取服務,而與標準執行緒池一起使用的 Servlet 則使用 Servlet 非同步 API 存取服務。雖然沒有涉及任何網路 IO,但這不應影響結果。
最初的測試不出所料地顯示,阻塞方法和非同步方法之間沒有可衡量的差異,因為時間主要受 5 秒延遲的影響。為了在沒有延遲影響的情況下探索差異,將延遲減少到零,並執行了一組與吞吐量測試類似的測試。結果如下圖所示
我們再次看到,虛擬執行緒通常效能更高,差異在低並行性和並行性超過測試可用的處理器核心數量時最為明顯。
基於虛擬執行緒的執行器與 Tomcat 標準執行緒池之間的差異,並不像從上面的圖表中最初看到的那樣明顯。這些測試旨在檢視與每種方法相關的開銷,並不能代表真實世界的應用程式。在真實世界的應用程式中,與完成請求所需的時間相比,測試中顯示的差異可能微不足道。
Tomcat 標準執行緒池與基於虛擬執行緒的執行器之間效能差異的主要驅動因素是,在執行緒池佇列中新增和移除任務時的競爭。透過最佳化 Tomcat 目前使用的實作,有可能減少標準執行緒池佇列中的競爭並提高吞吐量。
影響相對效能的次要因素是上下文切換。這很可能是第二個實驗中看到當並行性超過可用處理器核心數量時效能差異的解釋,因為虛擬執行緒的上下文切換成本低於標準執行緒池中的執行緒。
使用基於虛擬執行緒的執行器是 Tomcat 標準執行緒池的可行替代方案。就容器開銷而言,切換到虛擬執行緒執行器的優勢微乎其微。
Web 應用程式(例如 Tomcat 上的傳統 Spring MVC)遇到阻塞,並且尚未切換到 Servlet 非同步 API、反應式程式設計或其他非同步 API,則應透過切換到基於虛擬執行緒的執行器來看到一些可擴展性改進。根據 Web 應用程式的不同,這些改進可能在無需變更 Web 應用程式程式碼的情況下實現。
已切換到使用 Servlet 非同步 API、反應式程式設計或其他非同步 API 的 Web 應用程式,不太可能透過切換到基於虛擬執行緒的執行器來觀察到可衡量的差異(正面或負面)。
從長遠來看,虛擬執行緒的最大優勢似乎是更簡單的應用程式程式碼。目前需要使用 Servlet 非同步 API、反應式程式設計或其他非同步 API 的某些用例,將能夠透過使用阻塞 IO 和虛擬執行緒來滿足。但需要注意的是,應用程式通常需要多次呼叫不同的外部服務。這可以透過並行方式最有效地完成,雖然像 Project Reactor 這樣的框架為此提供了一流的支援,但 JRE 中用於此目的的等效解決方案(結構化並行)仍處於孵化階段,並且僅旨在協調多個 future,而不是以最方便的方式宣告或組合它們。
最後,Project Loom 仍處於預覽模式。現在考慮在生產環境中使用虛擬執行緒還為時過早,但現在是將 Project Loom 和虛擬執行緒納入您的規劃的時候,以便您在 JRE 中普遍提供虛擬執行緒時做好準備。
測試環境包含以下內容
測試在完全更新的 Ubuntu 22.04.1 LTS 機器上執行,該機器配備 Intel i7-6950X 處理器和 32 GB RAM。
為了最大程度地提高測試之間差異的可見性,從預設設定進行了以下組態變更,以最大限度地減少常見開銷
測試 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