Performance comparison of Spring Boot virtual threads and Webflux on JWT verification and MySQL query

I saw an article about the performance comparison between Spring Boot virtual threads and Webflux this morning, and I thought it was pretty good. The content is long, so I won’t translate it. Instead, I’ll focus on introducing the core content of this article to you so that you can read it quickly.

testing scenarios

The author uses a scenario that is as close to reality as possible:

  1. Extract JWT from authorization header
  2. Validate the JWT and extract the user's Email from it
  3. Use the user's email to execute the query in MySQL
  4. Return user record

testing technology

The two core technical points to be compared here are:

  1. Spring Boot with virtual threads: This is not a Spring Boot application running on traditional physical threads, but on virtual threads. These lightweight threads simplify the complex tasks of developing, maintaining, and debugging high-throughput concurrent applications. Although virtual threads still run on underlying operating system threads, they bring significant efficiency improvements. When a virtual thread encounters a blocking I/O operation, the Java runtime temporarily suspends it, freeing the associated operating system thread to service other virtual threads. This elegant solution optimizes resource allocation and enhances overall application responsiveness.
  2. Spring Boot Webflux: Spring Boot WebFlux is a reactive programming framework in the Spring ecosystem that leverages the Project Reactor library to implement non-blocking, event-driven programming. Therefore, it is particularly suitable for applications that require high concurrency and low latency. Relying on a reactive approach, it allows developers to efficiently handle large numbers of concurrent requests while still providing the flexibility to integrate with a variety of data sources and communication protocols.

Whether it is Webflux or virtual threads, both are designed to provide high concurrency capabilities for programs, so who is better? Let’s take a look at the specific tests.

test environment

Operating environment and tools

  • A MacBook Pro M1 with 16G memory
  • Java 20
  • Spring Boot 3.1.3
  • Enable preview mode to gain the power of virtual threads
  • Dependent third-party libraries: jjwt, mysql-connector-java
  • Test Tool: Bombardier
  • Database: MySQL

data preparation

  • Prepare a list of 100,000 JWTs in Bombardier to randomly select JWTs from them and put them into the authorization information of the HTTP request.
  • Create a users table in MySQL with the following table structure:
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)
  • Prepare 100,000 user data for the users table

test code

Spring Boot program with virtual threads

application.propertiesConfiguration file:

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

UserEntity class (in order to make the article more concise, DD omits the getter and setter here):

@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;

}

Application main class:

@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());
        };
    }
}

Provides CRUD operations UserRepository:

import org.springframework.data.repository.CrudRepository;
import com.example.demo.User;

public interface UserRepository extends CrudRepository<User, String> {

}

Classes that provide API interfaces 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 program

application.propertiesConfiguration file:

server.port=3000

spring.r2dbc.url=r2dbc:mysql://localhost:3306/testdb
spring.r2dbc.username=dbser
spring.r2dbc.password=dbpwd

UserEntity (here DD also omits the constructor, getter and setter):

public class User {

  @Id
  private String email;

  private String first;

  private String last;

  private String city;

  private String county;

  private int age;

  // 省略了构造函数、getter、setter
  
}

Application main class:

@EnableWebFlux
@SpringBootApplication
public class UserApplication {

  public static void main(String[] args) {
    SpringApplication.run(UserApplication.class, args);
  }

}

Provides CRUD operations UserRepository:

public interface UserRepository extends R2dbcRepository<User, String> {

}

Provides checking the user's business class based on ID UserService:

@Service
public class UserService {

  @Autowired
  UserRepository userRepository;

  public Mono<User> findById(String id) {
    return userRepository.findById(id);
  }
}

Classes that provide API interfaces 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"));
  }

}

Test Results

Next comes the highlight. The author tested both technical solutions with 5 million requests. The different concurrent connection levels evaluated include: 50, 100, and 300.

The specific results are as follows:

50 concurrent connections

100 concurrent connections

300 concurrent connections

Finally, the author concluded that Spring Boot Webflux is better than Spring Boot with virtual threads.

It seems that the introduction of virtual threads is not as good as the Webflux already in use? I wonder if you have done any relevant research? If so, please feel free to chat with us in the message area~

What if you encounter difficulties while studying? You can join our super-high-quality Spring technology exchange group to participate in exchanges and discussions for better learning and progress! For more Spring Boot tutorials, click here! , welcome to collect and forward for support! If you are interested in the original text of this content, you can also click here to view it .

Welcome to follow my public account: Programmer DD. Be the first to understand cutting-edge industry news, share in-depth technical information, and obtain high-quality learning resources

JetBrains releases Rust IDE: RustRover Java 21 / JDK 21 (LTS) GA With so many Java developers in China, an ecological-level application development framework .NET 8 should be born. The performance is greatly improved, and it is far ahead of .NET 7. PostgreSQL 16 is released by a former member of the Rust team I deeply regret and asked to cancel my name. I completed the removal of Nue JS on the front end yesterday. The author said that I will create a new Web ecosystem. NetEase Fuxi responded to the death of an employee who was "threatened by HR due to BUG". Ren Zhengfei: We are about to enter the fourth industrial revolution, Apple Is Huawei's teacher Vercel's new product "v0": Generate UI interface code based on text
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/didispace/blog/10111993