環(huán)境:SpringBoot3.2.1 + JDK21
從Spring Boot 3.2 支持虛擬線程。要使用虛擬線程,需要在 Java 21 上運行,并將屬性 spring.threads.virtual.enabled 設(shè)置為 true。
啟用虛擬線程后,Tomcat 和 Jetty 將使用虛擬線程處理請求。這意味著處理網(wǎng)絡請求的應用程序代碼(如控制器中的方法)將在虛擬線程上運行。
啟用虛擬線程后,applicationTaskExecutor Bean 將成為配置為使用虛擬線程的 SimpleAsyncTaskExecutor。任何使用應用程序任務執(zhí)行器的地方,如調(diào)用 @Async 方法時的 @EnableAsync、Spring MVC 的異步請求處理和 Spring WebFlux 的阻塞執(zhí)行支持,現(xiàn)在都將使用虛擬線程。
接下來將分別通過傳統(tǒng)阻塞Servlet技術(shù)、使用虛擬線程及使用反應式技術(shù)WebFlux來分別對比它們的性能。
使用虛擬線程 & 傳統(tǒng)Servlet都使用下面的接口:
@RestController@RequestMapping("/task/default")public class TaskDefaultController { @GetMapping("") public Object index() throws Exception { System.out.printf("before - %s%n", Thread.currentThread()) ; TimeUnit.MILLISECONDS.sleep(100) ; System.out.printf("after - %s%n", Thread.currentThread()) ; return "task - default..." ; }}
先測試下啟用虛擬線程執(zhí)行情況。
配置:
spring: threads: virtual: enabled: true
控制臺輸出:
before - VirtualThread[#42,tomcat-handler-0]/runnable@ForkJoinPool-1-worker-1after - VirtualThread[#42,tomcat-handler-0]/runnable@ForkJoinPool-1-worker-1
使用的是虛擬線程。
配置線程池,如果不配置使用默認的最大線程200,整體的吞吐量將在2200作用。
server: tomcat: threads: min-spare: 500 max: 1000
初始啟動服務后,內(nèi)存,CPU占用情況;默認啟動后線程個數(shù)與上面配置一致。
圖片
使用jmeter測試,配置如下:
圖片
使用500個線程,循環(huán)200次,整體做100000次壓測。后續(xù)的測試都會基于該配置進行。
圖片
吞吐量為:4696
內(nèi)存,CPU占用情況
圖片
首先開啟虛擬線程
spring: threads: virtual: enabled: true
初始啟動服務后,內(nèi)存,CPU占用情況
圖片
jmeter測試情況如下:
圖片
吞吐量為:4677,與上面的阻塞Servlet基本差不多。但傳統(tǒng)Tomcat線程池方式需要更多的線程才能達到這一值。
圖片
整個過程內(nèi)存使用情況,虛擬線程要比傳統(tǒng)Tomcat線程池方式占用的多。
JDK 的虛擬線程調(diào)度器是一個工作偷取 ForkJoinPool,以先進先出(FIFO)模式運行。調(diào)度器的并行性是指可用來調(diào)度虛擬線程的平臺線程數(shù)。默認情況下,它等于可用處理器的數(shù)量,但可以通過系統(tǒng)屬性 jdk.virtualThreadScheduler.parallelism 進行調(diào)整。ForkJoinPool 與普通池不同,普通池用于并行流的實現(xiàn),并以后進先出模式運行。
調(diào)整數(shù)量再進行測試,設(shè)置JVM參數(shù)
-Djdk.virtualThreadScheduler.parallelism=100 -Djdk.virtualThreadScheduler.maxPoolSize=100
設(shè)置100個平臺線程來調(diào)用虛擬線程。
啟動服務后,線程,內(nèi)存使用情況。
圖片
jmeter測試結(jié)果如下:
圖片
與調(diào)整前沒什么區(qū)別,反而是增加了應用的線程數(shù)量。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId></dependency>
基于webflux,我們需要重新編寫接口測試。
@RestController@RequestMapping("/task/reactor")public class ReactorController { @GetMapping("") public Object index() throws Exception { // 與上面2種方式不同,reactor方式則需要使用delayElement方式來模擬耗時任務 return Mono.just("task - reactor...").delayElement(Duration.ofMillis(100)) ; }}
初始啟動服務后,內(nèi)存,CPU占用情況。
圖片
jmeter測試情況如下:
圖片
吞吐量為:4659,與上面的測試結(jié)果基本一致。
圖片
內(nèi)存使用情況要比前面幾種方式占用都少。同時通過jmeter測試結(jié)果也能發(fā)現(xiàn),MAX請求的最大響應時間webflux是最小的,Std.Dev:所有請求響應時間的標準差也是最小的(該值越小,平均值越可靠)。
根據(jù)測試結(jié)果,虛擬線程與webflux誰更勝一籌還不夠清晰,接下來我們結(jié)合數(shù)據(jù)庫操作進行測試。
數(shù)據(jù)庫數(shù)據(jù)準備了600w的數(shù)據(jù)。
圖片
基于JPA進行數(shù)據(jù)庫的操作
@Entity@Table(name = "t_user")public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer uid ; private String name ;}
Repository接口
public interface UserRepository extends JpaRepository<User, Integer> {}
Controller測試接口
@RestController@RequestMapping("/users")public class UserController { @Resource private UserRepository ur ; @GetMapping("/count") public User count() { return ur.findById(5800000).orElse(null) ; } }
測試結(jié)果:
圖片
記得開啟虛擬線程,測試結(jié)果如下:
圖片
需要引入如下依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-r2dbc</artifactId></dependency><dependency> <groupId>com.github.jasync-sql</groupId> <artifactId>jasync-r2dbc-mysql</artifactId> <version>2.1.24</version></dependency>
配置
spring: r2dbc: url: r2dbc:mysql://localhost:3306/batch?serverZnotallow=GMT%2B8&sslMode=DISABLED username: root password: xxxooo pool: initialSize: 100 maxSize: 100 max-acquire-time: 30s max-idle-time: 30m
實體定義,這里的注解與jpa不一樣
@Table("t_user")public class User { @Id private Integer uid ; private String name ;}
Repository定義
public interface UserR2DBCRepository extends ReactiveCrudRepository<User, Integer> {}
Controller接口
@RestController@RequestMapping("/r2dbc")public class UserR2DBCController { @Resource private UserR2DBCRepository ur ; @GetMapping("/users") public Mono<User> count() { return ur.findById(5800000) ; } }
測試結(jié)果
圖片
根據(jù)測試結(jié)果來,webflux的整體性能遠遠高于虛擬線程及傳統(tǒng)tomcat線程池的方式。
以上是本篇文章全部內(nèi)容,希望對你有幫助。
完畢!!!
本文鏈接:http://www.www897cc.com/showinfo-26-70431-0.htmlSpringBoot3虛擬線程 & 反應式(WebFlux) & 傳統(tǒng)Tomcat線程池性能對比
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: C++實現(xiàn)多功能計算器
下一篇: 一個用著方便的超強Python解釋器