日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差

發(fā)布時(shí)間:2024/1/21 javascript 52 coder
生活随笔 收集整理的這篇文章主要介紹了 MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

之前已經(jīng)分享過(guò)多篇關(guān)于Spring Boot中使用Java 21新特性虛擬線程的性能測(cè)試案例:

  • Spring Boot 3.2虛擬線程搭建靜態(tài)文件服務(wù)器有多快?
  • Spring Boot 虛擬線程與Webflux在JWT驗(yàn)證和MySQL查詢上的性能比較

早上看到群友問到一個(gè)關(guān)于虛擬線程遇到MySQL連接不兼容導(dǎo)致的性能問題:

這個(gè)問題確實(shí)之前就有看到過(guò)相關(guān)的評(píng)測(cè),順著個(gè)這個(gè)問題,重新把相關(guān)評(píng)測(cè)找出來(lái),給大家分享一下。

以下內(nèi)容主要參考文章:https://medium.com/deno-the-complete-reference/springboot-physical-vs-virtual-threads-vs-webflux-performance-comparison-for-jwt-verify-and-mysql-23d773b41ffd

評(píng)測(cè)案例

評(píng)測(cè)采用現(xiàn)實(shí)場(chǎng)景中的處理流程,具體如下:

  1. 從HTTP授權(quán)標(biāo)頭(authorization header)中提取 JWT
  2. 驗(yàn)證 JWT 并從中提取用戶的電子郵件
  3. 使用提取到的電子郵件執(zhí)行 MySQL 查詢用戶
  4. 返回用戶記錄

這個(gè)場(chǎng)景其實(shí)是Spring Boot 虛擬線程與Webflux在JWT驗(yàn)證和MySQL查詢上的性能比較測(cè)試的后續(xù)。前文主要對(duì)比了虛擬線程和WebFlux的,但沒有對(duì)比虛擬線程與物理線程的區(qū)別。所以,接下來(lái)的內(nèi)容就是本文關(guān)心的重點(diǎn):在物理線程和虛擬線程下,MySQL驅(qū)動(dòng)是否有性能優(yōu)化。

測(cè)試環(huán)境

  • Java 20(使用預(yù)覽模式,開啟虛擬線程)
  • Spring Boot 3.1.3
  • 依賴的第三方庫(kù):jjwt、mysql-connector-java

測(cè)試工具:Bombardier

采用了開源負(fù)載測(cè)試工具:Bombardier。在測(cè)試場(chǎng)景中預(yù)先創(chuàng)建 100,000 個(gè) JWT 列表。

在測(cè)試期間,Bombardier 從該池中隨機(jī)選擇了JWT,并將它們包含在HTTP請(qǐng)求的Authorization標(biāo)頭中。

MySQL表結(jié)構(gòu)與數(shù)據(jù)準(zhǔn)備

User表結(jié)構(gòu)如下:

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)

準(zhǔn)備大約10w條數(shù)據(jù):

mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
|    99999 |
+----------+
1 row in set (0.01 sec)

測(cè)試代碼:使用物理線程

配置文件:

server.port=3000
spring.datasource.url= jdbc:mysql://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

User實(shí)體定義:

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

  // 省略了getter和setter
}

數(shù)據(jù)訪問實(shí)現(xiàn):

public interface UserRepository extends CrudRepository<User, String> {

}

API實(shí)現(xiàn):

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

應(yīng)用主類:

@SpringBootApplication
public class UserApplication {

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

測(cè)試代碼:使用虛擬線程

主要調(diào)整應(yīng)用主類,其他一樣,具體修改如下:

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

測(cè)試代碼:使用WebFlux

server.port=3000
spring.r2dbc.url=r2dbc:mysql://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
@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;

  // 省略getter、setter和構(gòu)造函數(shù)
}

數(shù)據(jù)訪問實(shí)現(xiàn):

public interface UserRepository extends R2dbcRepository<User, String> {

}

業(yè)務(wù)邏輯實(shí)現(xiàn):

@Service
public class UserService {

  @Autowired
  UserRepository userRepository;

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

API實(shí)現(xiàn):

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

應(yīng)用主類:

@EnableWebFlux
@SpringBootApplication
public class UserApplication {

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

}

測(cè)試結(jié)果

每次測(cè)試都包含 100 萬(wàn)個(gè)請(qǐng)求,分別評(píng)估了它們?cè)诓煌l(fā)(50、100、300)水平下的性能。下面是結(jié)果展示:

分析總結(jié)

在這個(gè)測(cè)試案例中使用了MySQL驅(qū)動(dòng),虛擬線程的實(shí)現(xiàn)方式性能最差,WebFlux依然保持領(lǐng)先。所以,主要原因在于這個(gè)MySQL的驅(qū)動(dòng)對(duì)虛擬線程不友好。如果涉及到數(shù)據(jù)庫(kù)訪問的情況下,需要尋找對(duì)虛擬線程支持最佳的驅(qū)動(dòng)程序。另外,該測(cè)試使用的是Java 20和Spring Boot 3.1。對(duì)于Java 21和Spring Boot 3.2建議讀者在使用的時(shí)候自行評(píng)估。

最后,對(duì)于MySQL驅(qū)動(dòng)對(duì)虛擬線程支持好的,歡迎留言區(qū)推薦一下。如果您學(xué)習(xí)過(guò)程中如遇困難?可以加入我們超高質(zhì)量的Spring技術(shù)交流群,參與交流與討論,更好的學(xué)習(xí)與進(jìn)步!更多Spring Boot教程可以點(diǎn)擊直達(dá)!,歡迎收藏與轉(zhuǎn)發(fā)支持!

歡迎關(guān)注我的公眾號(hào):程序猿DD。第一時(shí)間了解前沿行業(yè)消息、分享深度技術(shù)干貨、獲取優(yōu)質(zhì)學(xué)習(xí)資源

總結(jié)

以上是生活随笔為你收集整理的MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。