Spring?Boot虛擬線程Webflux在JWT驗證和MySQL查詢性能比較
引言
看到一篇關于Spring Boot虛擬線程和Webflux性能對比的文章,覺得還不錯。內容較長,我就不翻譯了,抓重點給大家介紹一下這篇文章的核心內容,方便大家快速閱讀。
測試場景
作者采用了一個盡可能貼近現(xiàn)實操作的場景:
- 從授權頭信息中提取JWT
- 驗證JWT并從中提取用戶的Email
- 使用用戶的Email去MySQL里執(zhí)行查詢
- 返回用戶記錄
測試技術
這里要對比的兩個核心技術點是:
- 帶有虛擬線程的Spring Boot:這不是一個跑在傳統(tǒng)物理線程上的Spring Boot應用,而是跑在虛擬線程上的。這些輕量級線程簡化了開發(fā)、維護和調試高吞吐量并發(fā)應用程序的復雜任務。雖然虛擬線程仍然在底層操作系統(tǒng)線程上運行,但它們帶來了顯著的效率改進。當虛擬線程遇到阻塞 I/O 操作時,Java 運行時會暫時掛起它,從而釋放關聯(lián)的操作系統(tǒng)線程來為其他虛擬線程提供服務。這個優(yōu)雅的解決方案優(yōu)化了資源分配并增強了整體應用程序響應能力。
- Spring Boot Webflux:Spring Boot WebFlux是Spring生態(tài)系統(tǒng)中的反應式編程框架,它利用Project Reactor庫來實現(xiàn)非阻塞、事件驅動的編程。所以,它特別適合需要高并發(fā)和低延遲的應用程序。依靠反應式方法,它允許開發(fā)人員有效地處理大量并發(fā)請求,同時仍然提供與各種數(shù)據(jù)源和通信協(xié)議集成的靈活性。
不論是Webflux還是虛擬線程,這兩個都是為了提供程序的高并發(fā)能力而生,那么誰更勝一籌呢?下面一起看看具體的測試。
測試環(huán)境
運行環(huán)境與工具
- 一臺16G內存的MacBook Pro M1
- Java 20
- Spring Boot 3.1.3
- 啟用預覽模式,以獲得虛擬線程的強大能力
- 依賴的第三方庫:jjwt、mysql-connector-java
- 測試工具:Bombardier
- 數(shù)據(jù)庫:MySQL
數(shù)據(jù)準備
- 在Bombardier中準備100000個JWT列表,用來從中隨機選取JWT,并將其放入HTTP請求的授權信息中。
- 在MySQL中創(chuàng)建一個users表,表結構如下:
mysql> desc users; +--------+--------------+------+-----+---------+-------+ | 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 | | +--------+--------------+------+-----+---------+-------+ 6 rows in set (0.00 sec)
- 為users表準備100000條用戶數(shù)據(jù)
測試代碼
帶虛擬線程的Spring Boot程序
application.properties
配置文件:
server.port=3000 spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false spring.datasource.username= testuser spring.datasource.password= testpwd spring.jpa.hibernate.ddl-auto= update spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
User
實體類(為了讓文章讓簡潔一些,這里DD省略了getter和setter):
@Entity @Table(name = "users") public class User { @Id private String email; private String first; private String last; private String city; private String county; private int age; }
應用主類:
@SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } @Bean public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() { return protocolHandler -> { protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); }; } }
提供CRUD操作的UserRepository
:
import org.springframework.data.repository.CrudRepository; import com.example.demo.User; public interface UserRepository extends CrudRepository<User, String> { }
提供API接口的UserController
類:
@RestController public class UserController { @Autowired UserRepository userRepository; private SignatureAlgorithm sa = SignatureAlgorithm.HS256; private String jwtSecret = System.getenv("JWT_SECRET"); @GetMapping("/") public User handleRequest(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHdr) { String jwtString = authHdr.replace("Bearer",""); Claims claims = Jwts.parser() .setSigningKey(jwtSecret.getBytes()) .parseClaimsJws(jwtString).getBody(); Optional<User> user = userRepository.findById((String)claims.get("email")); return user.get(); } }
Spring Boot Webflux程序
application.properties
配置文件:
server.port=3000 spring.r2dbc.url=r2dbc:mysql://localhost:3306/testdb spring.r2dbc.username=dbser spring.r2dbc.password=dbpwd
User
實體(這里DD也省略了構造函數(shù)、getter和setter):
public class User { @Id private String email; private String first; private String last; private String city; private String county; private int age; // 省略了構造函數(shù)、getter、setter }
應用主類:
@EnableWebFlux @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }
提供CRUD操作的UserRepository
:
public interface UserRepository extends R2dbcRepository<User, String> { }
提供根據(jù)id查用戶的業(yè)務類UserService
:
@Service public class UserService { @Autowired UserRepository userRepository; public Mono<User> findById(String id) { return userRepository.findById(id); } }
提供API接口的UserController
類:
@RestController @RequestMapping("/") public class UserController { @Autowired UserService userService; private SignatureAlgorithm sa = SignatureAlgorithm.HS256; private String jwtSecret = System.getenv("JWT_SECRET"); @GetMapping("/") @ResponseStatus(HttpStatus.OK) public Mono<User> getUserById(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHdr) { String jwtString = authHdr.replace("Bearer",""); Claims claims = Jwts.parser() .setSigningKey(jwtSecret.getBytes()) .parseClaimsJws(jwtString).getBody(); return userService.findById((String)claims.get("email")); } }
測試結果
接下來是重頭戲了,作者對兩個技術方案都做了500w個請求的測試,評估的不同并發(fā)連接級別包含:50、100、300。
具體結果如下三張圖:
結論
Spring Boot Webflux要更優(yōu)于帶虛擬線程的Spring Boot。
似乎引入了虛擬線程還不如已經在用的Webflux?不知道大家是否有做過相關調研呢?
以上就是Spring Boot虛擬線程與Webflux在JWT驗證和MySQL查詢上的性能比較的詳細內容,更多關于Spring Boot虛擬線程與Webflux在JWT驗證和MySQL查詢上的性能比較的資料請關注腳本之家其它相關文章!
相關文章
Java.lang.NullPointerException的錯誤解決
Java中NullPointerException是一種常見的運行時異常,通常發(fā)生在嘗試調用null對象的方法或訪問其屬性時,具有一定的參考價值,感興趣的可以了解一下2024-09-09