Spring Boot虚拟线程的性能还不如Webflux?

早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错 。内容较长 , 抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读 。
测试场景作者采用了一个尽可能贴近现实操作的场景:

  1. 从授权头信息中提取JWT
  2. 验证JWT并从中提取用户的EmAIl
  3. 使用用户的Email去MySQL里执行查询
  4. 返回用户记录
测试技术这里要对比的两个核心技术点是:
  1. 带有虚拟线程的Spring Boot:这不是一个跑在传统物理线程上的Spring Boot应用,而是跑在虚拟线程上的 。这些轻量级线程简化了开发、维护和调试高吞吐量并发应用程序的复杂任务 。虽然虚拟线程仍然在底层操作系统线程上运行,但它们带来了显著的效率改进 。当虚拟线程遇到阻塞 I/O 操作时,JAVA 运行时会暂时挂起它 , 从而释放关联的操作系统线程来为其他虚拟线程提供服务 。这个优雅的解决方案优化了资源分配并增强了整体应用程序响应能力 。
  2. Spring Boot Webflux:Spring Boot WebFlux是Spring生态系统中的反应式编程框架,它利用Project Reactor库来实现非阻塞、事件驱动的编程 。所以,它特别适合需要高并发和低延迟的应用程序 。依靠反应式方法,它允许开发人员有效地处理大量并发请求,同时仍然提供与各种数据源和通信协议集成的灵活性 。
不论是Webflux还是虚拟线程 , 这两个都是为了提供程序的高并发能力而生 , 那么谁更胜一筹呢?下面一起看看具体的测试 。
测试环境运行环境与工具
  • 一台16G内存的macBook Pro M1
  • Java 20
  • Spring Boot 3.1.3
  • 启用预览模式 , 以获得虚拟线程的强大能力
  • 依赖的第三方库:jjwt、mysql-connector-java
  • 测试工具:Bombardier
  • 数据库:MySQL
数据准备
  • 在Bombardier中准备100000个JWT列表,用来从中随机选取JWT,并将其放入HTTP请求的授权信息中 。
  • 在MySQL中创建一个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条用户数据
测试代码带虚拟线程的Spring Boot程序Application.properties配置文件:
server.port=3000spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=falsespring.datasource.username= testuserspring.datasource.password= testpwdspring.jpa.hibernate.ddl-auto= updatespring.datasource.driver-class-name=com.mysql.cj.jdbc.DriverUser实体类(为了让文章让简洁一些 , 这里DD省略了getter和setter):
@Entity@Table(name = "users")public class User {@Idprivate String email;private String first;private String last;private String city;private String county;private int age;}应用主类:
@SpringBootApplicationpublic class UserApplication {public static void main(String[] args) {SpringApplication.run(UserApplication.class, args);}@Beanpublic 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类:
@RestControllerpublic class UserController {@AutowiredUserRepository 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();}}


推荐阅读