色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

SpringBoot物理線程、虛擬線程、Webflux性能比較

jf_ro2CN3Fa ? 來源:叢林 medium.com ? 2023-10-23 14:31 ? 次閱讀

來源:叢林medium.com

大量的文章評估了一系列技術(包括 Node.js、Deno、Bun、Rust、Go、Spring、Python 等)在簡單的“hello world”場景中的性能。雖然這些文章獲得了好評,但有一個共同點:忽略了現實場景開發中的復雜性

本文旨在通過現實場景的視角剖析各種技術,在這種特殊情況下,我們深入研究以下常見用例:

從 authorization header 中提取一個JWT。

驗證JWT并從聲明中提取用戶的電子郵件。

使用提取的電子郵件執行MySQL查詢。

最后,返回用戶的記錄。

雖然這個場景看起來似乎也很簡單,但它概括了 Web 開發領域中經常遇到的現實挑戰。

介紹

在本文中,我們將深入探討所有同級產品之間的友好比較,即具有「物理線程、虛擬線程和 Webflux 的 SpringBoot」 ,重點關注它們在特定用例場景中的性能。我們已經探索了標準 SpringBoot 應用程序如何與 webflux 相媲美,但現在,我們引入一個關鍵的區別:

帶有虛擬線程的 Spring Boot

我們熟悉 SpringBoot,但有一點不同——它在虛擬線程而不是傳統的物理線程上運行。虛擬線程是并發領域的游戲規則改變者。這些輕量級線程簡化了開發、維護和調試高吞吐量并發應用程序的復雜任務。

雖然虛擬線程仍然在底層操作系統線程上運行,但它們帶來了顯著的效率改進。當虛擬線程遇到阻塞 I/O 操作時,Java 運行時會暫時掛起它,從而釋放關聯的操作系統線程來為其他虛擬線程提供服務。這個優雅的解決方案優化了資源分配并增強了整體應用程序響應能力。

考慮到這些有趣的設置,讓我們更深入地研究我們的性能比較。撰寫本文是為了解決最常見的請求之一,即查看物理、虛擬和 Webflux 在實際用例中的比較。

測試環境及軟件版本

我們的性能測試是在配備 16GB RAM 的 MacBook Pro M1 上進行的,確保了可靠的測試平臺。用于這些測試的軟件堆棧包括:

SpringBoot 3.1.3(在Java 20上運行)

啟用預覽模式以獲得虛擬線程的強大功能

jjwt用于JWT驗證和解碼,增強我們應用程序的安全性。

mysql-connector-java 用于執行 MySQL 查詢,維護數據完整性和一致性。

負載測試和 JWT

為了評估我們的應用程序在不同負載下的性能,我們使用了開源負載測試工具 Bombardier。我們的測試場景涉及預先創建的 100000 個 JWT 列表。在測試過程中,Bombardier 從該池中隨機選擇 JWT,并將它們包含在 HTTP 請求的授權標頭中。

MySQL 數據庫架構

用于這些性能測試的 MySQL 數據庫有一個名為 users 的表。該表設計有 6 列,足以模擬我們應用程序中的真實數據交互,使我們能夠評估它們的響應能力和可擴展性。

mysql>descusers;
+--------+--------------+------+-----+---------+-------+
|Field|Type|Null|Key|Default|Extra|
+--------+--------------+------+-----+---------+-------+
|email|varchar(255)|NO|PRI|NULL||
|first|varchar(255)|YES||NULL||
|last|varchar(255)|YES||NULL||
|city|varchar(255)|YES||NULL||
|county|varchar(255)|YES||NULL||
|age|int|YES||NULL||
+--------+--------------+------+-----+---------+-------+
6rowsinset(0.00sec)

用戶數據庫已準備好包含 100000 條用戶記錄的初始數據集。

mysql>selectcount(*)fromusers;
+----------+
|count(*)|
+----------+
|99999|
+----------+
1rowinset(0.01sec)

在我們對 SpringBoot 物理線程、虛擬線程和 Webflux 進行友好性能評估的背景下,了解關鍵的數據關系至關重要。具體來說,在JSON Web Token(JWT)有效負載中,每個電子郵件條目直接對應于存儲在 MySQL 數據庫中的一條用戶記錄。

代碼

SpringBoot(物理線程)

配置信息

server.port=3000
spring.datasource.url= jdbc//localhost:3306/testdb?useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username= dbuser
spring.datasource.password= dbpwd
spring.jpa.hibernate.ddl-auto= update
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

實體類

packagecom.example.demo;

importjakarta.persistence.Entity;
importjakarta.persistence.Table;
importjakarta.persistence.GeneratedValue;
importjakarta.persistence.GenerationType;
importjakarta.persistence.Id;

@Entity
@Table(name="users")
publicclassUser{
@Id
privateStringemail;

privateStringfirst;

privateStringlast;

privateStringcity;

privateStringcounty;

privateintage;

publicStringgetId(){
returnemail;
}

publicvoidsetId(Stringemail){
this.email=email;
}

publicStringgetFirst(){
returnfirst;
}

publicvoidsetFirst(Stringname){
this.first=name;
}

publicStringgetLast(){
returnlast;
}

publicvoidsetLast(Stringname){
this.last=name;
}

publicStringgetEmail(){
returnemail;
}

publicvoidsetEmail(Stringemail){
this.email=email;
}

publicStringgetCity(){
returncity;
}

publicvoidsetCity(Stringcity){
this.city=city;
}

publicStringgetCounty(){
returncounty;
}

publicvoidsetCounty(Stringcounty){
this.county=county;
}

publicintgetAge(){
returnage;
}

publicvoidsetAge(intage){
this.age=age;
}
}

啟動類

packagecom.example.demo;

importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
importorg.springframework.context.annotation.Bean;

@SpringBootApplication
publicclassUserApplication{

publicstaticvoidmain(String[]args){
SpringApplication.run(UserApplication.class,args);
}
}

Controller層

packagecom.example.demo;

importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RequestHeader;
importorg.springframework.http.ResponseEntity;
importorg.springframework.http.HttpStatus;
importorg.springframework.http.HttpHeaders;
importorg.springframework.web.bind.annotation.RestController;
importorg.springframework.beans.factory.annotation.Autowired;
importjava.util.Optional;
importio.jsonwebtoken.Jwts;
importio.jsonwebtoken.Jws;
importio.jsonwebtoken.Claims;
importio.jsonwebtoken.SignatureAlgorithm;
importio.jsonwebtoken.security.Keys;
importjava.security.Key;
importcom.example.demo.UserRepository;
importcom.example.demo.User;

@RestController
publicclassUserController{

@Autowired
UserRepositoryuserRepository;

privateSignatureAlgorithmsa=SignatureAlgorithm.HS256;
privateStringjwtSecret=System.getenv("JWT_SECRET");

@GetMapping("/")
publicUserhandleRequest(@RequestHeader(HttpHeaders.AUTHORIZATION)StringauthHdr){
StringjwtString=authHdr.replace("Bearer","");
Claimsclaims=Jwts.parser()
.setSigningKey(jwtSecret.getBytes())
.parseClaimsJws(jwtString).getBody();

Optionaluser=userRepository.findById((String)claims.get("email"));
returnuser.get();
}
}

接口

packagecom.example.demo;

importorg.springframework.data.repository.CrudRepository;
importcom.example.demo.User;

publicinterfaceUserRepositoryextendsCrudRepository{

}

Springboot(虛擬線程)

其余代碼基本照搬上述 「物理線程」 , 啟動類修改如下:

packagecom.example.demo;

importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
importorg.springframework.context.annotation.Bean;
importjava.util.concurrent.Executors;

@SpringBootApplication
publicclassUserApplication{

publicstaticvoidmain(String[]args){
SpringApplication.run(UserApplication.class,args);
}

@Bean
publicTomcatProtocolHandlerCustomizerprotocolHandlerVirtualThreadExecutorCustomizer(){
returnprotocolHandler->{
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}

SpringBoot(webflux)

server.port=3000
spring.r2dbc.url=r2dbc//localhost:3306/testdb?allowPublicKeyRetrieval=true&ssl=false
spring.r2dbc.username=dbuser
spring.r2dbc.password=dbpwd
spring.r2dbc.pool.initial-size=10
spring.r2dbc.pool.max-size=10

啟動類

packagewebfluxdemo;

importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.context.annotation.Bean;
importorg.springframework.core.io.ClassPathResource;
importorg.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
importorg.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
importorg.springframework.web.reactive.config.EnableWebFlux;

importio.r2dbc.spi.ConnectionFactory;

@EnableWebFlux
@SpringBootApplication
publicclassUserApplication{

publicstaticvoidmain(String[]args){
SpringApplication.run(UserApplication.class,args);
}

}

Controller層代碼

packagewebfluxdemo;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.http.HttpStatus;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.PathVariable;
importorg.springframework.web.bind.annotation.RequestBody;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestParam;
importorg.springframework.web.bind.annotation.ResponseStatus;
importorg.springframework.web.bind.annotation.RestController;
importorg.springframework.web.bind.annotation.RequestHeader;
importorg.springframework.http.HttpHeaders;

importwebfluxdemo.User;
importwebfluxdemo.UserService;

importio.jsonwebtoken.Jwts;
importio.jsonwebtoken.Jws;
importio.jsonwebtoken.Claims;
importio.jsonwebtoken.SignatureAlgorithm;
importio.jsonwebtoken.security.Keys;
importjava.security.Key;

importreactor.core.publisher.Flux;
importreactor.core.publisher.Mono;

@RestController
@RequestMapping("/")
publicclassUserController{
@Autowired
UserServiceuserService;

privateSignatureAlgorithmsa=SignatureAlgorithm.HS256;
privateStringjwtSecret=System.getenv("JWT_SECRET");

@GetMapping("/")
@ResponseStatus(HttpStatus.OK)
publicMonogetUserById(@RequestHeader(HttpHeaders.AUTHORIZATION)StringauthHdr){
StringjwtString=authHdr.replace("Bearer","");
Claimsclaims=Jwts.parser()
.setSigningKey(jwtSecret.getBytes())
.parseClaimsJws(jwtString).getBody();
returnuserService.findById((String)claims.get("email"));
}
}

接口類

packagewebfluxdemo;

importorg.springframework.data.r2dbc.repository.R2dbcRepository;
importorg.springframework.stereotype.Repository;

importwebfluxdemo.User;

publicinterfaceUserRepositoryextendsR2dbcRepository{

}

Service層代碼

packagewebfluxdemo;

importjava.util.Optional;

importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Service;

importwebfluxdemo.User;
importwebfluxdemo.UserRepository;

importreactor.core.publisher.Flux;
importreactor.core.publisher.Mono;

@Service
publicclassUserService{

@Autowired
UserRepositoryuserRepository;

publicMonofindById(Stringid){
returnuserRepository.findById(id);
}
}

結果

為了評估性能,我們進行了一系列嚴格的測試。每個測試由100萬個請求組成,我們評估了它們在不同并發連接級別(50、100和300)下的性能。

現在,讓我們深入研究結果,以圖表形式呈現:

1d7bc8ae-6fea-11ee-939d-92fbcf53809c.png所用時間對比 1d94cd90-6fea-11ee-939d-92fbcf53809c.png每秒請求數 1daebb24-6fea-11ee-939d-92fbcf53809c.png最小延遲 1dc5fe88-6fea-11ee-939d-92fbcf53809c.png10%延遲 1de4b120-6fea-11ee-939d-92fbcf53809c.png25%延遲 1e05d51c-6fea-11ee-939d-92fbcf53809c.png平均延遲 1e194a70-6fea-11ee-939d-92fbcf53809c.png中位數延遲 1e29a0fa-6fea-11ee-939d-92fbcf53809c.png75%延遲 1e4672c0-6fea-11ee-939d-92fbcf53809c.png90%延遲 1e58d6a4-6fea-11ee-939d-92fbcf53809c.png99%延遲 1e6e8288-6fea-11ee-939d-92fbcf53809c.png最高延遲 1e7bc006-6fea-11ee-939d-92fbcf53809c.png平均CPU使用率 1e90a728-6fea-11ee-939d-92fbcf53809c.png平均內存使用率

分析

在此設置中,即使用MySQL驅動程序時,虛擬線程提供的性能最低、Webflux保持遙遙領先。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2966

    瀏覽量

    104702
  • 開源
    +關注

    關注

    3

    文章

    3309

    瀏覽量

    42471
  • MySQL
    +關注

    關注

    1

    文章

    804

    瀏覽量

    26530
  • 線程
    +關注

    關注

    0

    文章

    504

    瀏覽量

    19675
  • SpringBoot
    +關注

    關注

    0

    文章

    173

    瀏覽量

    177

原文標題:SpringBoot 物理線程、虛擬線程、Webflux 性能全面對比!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Spring Boot虛擬線程Webflux性能對比

    早上看到一篇關于Spring Boot虛擬線程Webflux性能對比的文章,覺得還不錯。內容較長,抓重點給大家介紹一下這篇文章的核心內容,方便大家快速閱讀。
    發表于 09-24 14:54 ?924次閱讀
    Spring Boot<b class='flag-5'>虛擬</b><b class='flag-5'>線程</b>和<b class='flag-5'>Webflux</b><b class='flag-5'>性能</b>對比

    .NET8性能優化之線程

    目前來說,沒有確切的證據證明哪個線程池好用,或者效率更高。但是開發者可以使用上面的選項來進行自己的選擇,有一個測試就是在Windows線程池在比較大的機器上的IO擴展性不太好。如果你的應用程序已經
    的頭像 發表于 01-22 14:50 ?1130次閱讀

    CPU與核心及進程和線程認識

    所謂的4核8線程,4核指的是物理核心。通過超線程技術,用一個物理核模擬兩個虛擬核,每個核兩個線程
    的頭像 發表于 03-30 14:48 ?8077次閱讀
    CPU與核心及進程和<b class='flag-5'>線程</b>認識

    虛擬機:linux 進程的最大線程個數

    虛擬機:linux 進程的最大線程個數
    的頭像 發表于 06-22 15:56 ?2742次閱讀
    <b class='flag-5'>虛擬</b>機:linux 進程的最大<b class='flag-5'>線程</b>個數

    虛擬機:Linux查看線程信息的步驟

    虛擬機:Linux查看線程信息的步驟
    的頭像 發表于 06-24 08:41 ?3539次閱讀
    <b class='flag-5'>虛擬</b>機:Linux查看<b class='flag-5'>線程</b>信息的步驟

    SpringBoot實現多線程

    SpringBoot實現多線程
    的頭像 發表于 01-12 16:59 ?1814次閱讀
    <b class='flag-5'>SpringBoot</b>實現多<b class='flag-5'>線程</b>

    什么是線程線程池中線程實現復用的原理

    一般建議自定義線程工廠,構建線程的時候設置線程的名稱,這樣就在查日志的時候就方便知道是哪個線程執行的代碼。
    發表于 01-29 13:44 ?1746次閱讀

    核心線程數和最大線程數區別

    達到最大線程數。當任務執行完畢后,線程池會根據線程池參數來決定是否回收線程。 簡單來說,核心線程數用于優化
    的頭像 發表于 06-01 09:33 ?7653次閱讀

    線程池的線程怎么釋放

    線程分組看,pool名開頭線程占616條,而且waiting狀態也是616條,這個點就非常可疑了,我斷定就是這個pool開頭線程池導致的問題。我們先排查為何這個線程池中會有600+的
    發表于 07-31 10:49 ?2274次閱讀
    <b class='flag-5'>線程</b>池的<b class='flag-5'>線程</b>怎么釋放

    Spring 的線程池應用

    。 使用@Async聲明多線程 SpringBoot 提供了注解 @Async 來使用線程池, 具體使用方法如下: 在啟動類(配置類)添加 @EnableAsync 來開啟線程池 在需
    的頭像 發表于 10-13 10:47 ?620次閱讀
    Spring 的<b class='flag-5'>線程</b>池應用

    什么是虛擬線程虛擬線程到底是做什么用的呢?

    虛擬線程是在Java并發領域添加的一個新概念,那么虛擬線程到底是做什么用的呢?
    的頭像 發表于 10-29 10:23 ?3135次閱讀
    什么是<b class='flag-5'>虛擬</b><b class='flag-5'>線程</b>?<b class='flag-5'>虛擬</b><b class='flag-5'>線程</b>到底是做什么用的呢?

    線程池基本概念與原理

    一、線程池基本概念與原理 1.1 線程池概念及優勢 C++線程池簡介 線程池是一種并發編程技術,它能有效地管理并發的線程、減少資源占用和提高
    的頭像 發表于 11-10 10:24 ?528次閱讀

    核心線程數和最大線程數怎么設置

    核心線程數和最大線程數是Java線程池中重要的參數,用來控制線程池中線程的數量和行為。正確地設置這兩個參數可以優化系統的
    的頭像 發表于 12-01 13:50 ?9043次閱讀

    redis多線程還能保證線程安全嗎

    Redis是一種使用C語言編寫的高性能鍵值存儲系統,它是單線程的,因為使用了多路復用的方式來處理并發請求。這樣的實現方式帶來了很好的性能,但同時也引發了一些線程安全方面的問題。 在Re
    的頭像 發表于 12-05 10:28 ?1796次閱讀

    探索虛擬線程:原理與實現

    虛擬線程的引入與優勢 在Loom項目之前,Java虛擬機(JVM)中的線程是通過java.lang.Thread類型來實現的,這些線程被稱為
    的頭像 發表于 06-24 11:35 ?288次閱讀
    探索<b class='flag-5'>虛擬</b><b class='flag-5'>線程</b>:原理與實現
    主站蜘蛛池模板: 忘忧草直播| 精品国产成a人在线观看| a视频免费在线| 最近更新2019中文字幕国语| 亚洲国产无线码在线观看| 色欲av蜜臀av高清| 色综合 亚洲 自拍 欧洲| 日本一本二本三区免费免费高清| 欧美三级aaa| 让人爽到湿的小黄书| 青青草原国产在线| 人妻无码AV中文系列| 日本久久精品免视看国产成人| 欧美人与禽zoz0性伦交app| 欧美牲交A欧美牲交VDO| 亚洲免费高清视频| 亚洲国产精品无码中文在线| 亚洲AV日韩AV欧美在线观看网| 亚洲国产AV精品一区二区蜜芽 | 成人免费观看在线视频| www.久艹| 成人在线精品视频| 国产精品福利片| 国产欧美一区二区三区在线看| 国产学生在线播放精品视频| 黑色丝袜美腿美女被躁翻了| 精品国产乱码久久久久久软件 | 国产一区亚洲| 九九热在线观看视频| 久久综合中文字幕佐佐木希| 免费a视频在线观看| 秋霞电影伦网理最新在线看片 | 日韩av无码在线直播| 天上人间影院久久国产| 亚洲大片免费| 在线观看国产高清免费不卡| 99热在线免费观看| 国产成人片视频一区二区青青 | JIZZ幻女大全| 国产精品人妻系列21P| 九九热精品在线|