前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >支持JDK19虚拟线程的web框架,上篇:体验

支持JDK19虚拟线程的web框架,上篇:体验

作者头像
程序员欣宸
发布2022-10-28 10:53:48
1.2K0
发布2022-10-28 10:53:48
举报
文章被收录于专栏:实战docker

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

关于虚拟线程

  • 随着JDK19 GA版本的发布,虚拟线程(来自Project Loom)这一特性也闪亮登场,虚拟线程是 JDK 而并非OS 实现的轻量级线程(Lightweight Process,LWP),许多虚拟线程共享同一操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量(可以参考golang的协程)
  • 虚拟线程方面的文章,如今已经有很多优秀博主写得非常详细深入,欣宸就不在这方面献丑了,而是将重点放在虚拟线程的实用性方面,用实战与大家一同学习:如何让虚拟线程在web服务中发挥作用,效果究竟如何?

关于支持虚拟线程的框架

  • Quarkus的web模块已经支持JDK19的虚拟线程了,收到web请求后,可以交给虚拟线程去处理,本身开发的工作量也不大,文章标题《支持JDK19虚拟线程的web框架》指的就是Quarkus框架,也是整个系列所用到的框架
  • 关于Quarkus,这是个强大的java框架,介绍Quarkus并非本文重点,有兴趣的读者可以关注欣宸的《quarkus实战》系列原创

本篇概览

  • Quarkus框架已支持使用虚拟线程处理web请求,欣宸原创的《支持JDK19虚拟线程的web框架》系列会陪伴大家,全程以实战动手为主,从快速体验,再到实际编码,以及部署构建都有详细说明,确保读者们既有成就感,又能学点小技能,在虚拟线程最火爆的时候抢先一步尝鲜,窥探未来技术的一角
  • 整个系列一共三篇文章,分别是《极速体验》、《细说开发》、《深入了解》,本篇是系列的开篇,主要内容是:不写代码,通过复制粘贴轻松完成最初的体验,把编码和设置这些繁重的工作留在后面的文章
  • 接下来说说本篇的主要内容:
  1. 整个体验环境,由quarkus应用和数据库组成,当我们向quarkus应用发起请求的时候,应用会去查询数据再将数据返回给前端
  2. quarkus应用提供了两个接口,第一个名为/vt/persons,使用虚拟线程来处理web请求,第二个名为/pool/persons,使用传统线程池来处理web请求,整体上如下图所示
image-20221016123754303
image-20221016123754303
  1. 由于quarkus对于响应式编程的支持比较完善,因此,quarkus应用中的web处理为响应式,并且数据库驱动也是响应式
  2. 上述基本功能调通后,我们就用压测工具来模拟并发访问,看看使用了虚拟线程的web服务性能究竟如何
  3. 再部署一个同样功能的SpringBoot应用,也做同样的压测,提供参考数据用于对比
  4. 最后画个图说明接下来具体要做的事
image-20221016182919235
image-20221016182919235

环境信息

  • 以下是本次实战的具体环境信息,给您作为参考
  1. 运行环境:一共用到了三台电脑,信息分别如下
image-20221016183707688
image-20221016183707688
  1. 上述三台电脑都用到了docker,按照官方文档部署最新版即可,我这里是20.10.19版本
  2. postgresql是用docker部署的,版本是15
  3. quarkus版本为2.13.2.Final,使用的JDK是19
  4. springboot版本为2.7.4,用到的JDK是11
  5. 压测工具k6为docker版,版本号v0.40.0
  • 省吃俭用攒了这么多电脑来搞事情,主要是想得到的压测结果尽量客观独立,不受太多干扰
  • 接下来,开始动手吧

部署数据库

  • 首先是安装数据库,确保宿主机上的docker已经部署好
  • 部署数据库很简单,一行命令即可,如下所示,除了服务,还新建了数据库,并且设置了账号和密码
代码语言:javascript
复制
sudo docker run \
--name postgres \
-p 5432:5432 \
-e POSTGRES_DB=quarkus_test \
-e POSTGRES_USER=quarkus \
-e POSTGRES_PASSWORD=123456 \
-d \
postgres:15

建表,添加数据

  • 用数据库工具连接数据库(我这里用的是IDEA的数据库工具),执行以下命令,会新建一个表,并导入6条记录
代码语言:javascript
复制
create table person (
  id serial primary key,
  name varchar(255),
  gender varchar(255),
  age int,
  external_id int
);
insert into person(name, age, gender, external_id) values('John Smith', 25, 'MALE', 10);
insert into person(name, age, gender, external_id) values('Paul Walker', 65, 'MALE', 20);
insert into person(name, age, gender, external_id) values('Lewis Hamilton', 35, 'MALE', 30);
insert into person(name, age, gender, external_id) values('Veronica Jones', 20, 'FEMALE', 40);
insert into person(name, age, gender, external_id) values('Anne Brown', 60, 'FEMALE', 50);
insert into person(name, age, gender, external_id) values('Felicia Scott', 45, 'FEMALE', 60);

部署quarkus应用

  • 登录用于部署quarkus应用的服务器,确保服务器上已经成功部署了docker
  • 新建名为application.properties的配置文件,内容是数据库相关的参数,如下所示
代码语言:javascript
复制
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus
quarkus.datasource.password=123456
quarkus.datasource.reactive.url=postgresql://192.168.0.99:5432/quarkus_test
  • 我这里,application.properties的路径是:/home/lighthouse/config/quarkus/application.properties
  • 检查application.properties文件的可读性,执行以下命令设置
代码语言:javascript
复制
sudo chmod a+r application.properties
  • 执行以下命令即可部署和启动quarkus应用,注意application.properties文件的位置要换成您自己的配置文件路径
代码语言:javascript
复制
docker run -idt \
--name quarkus \
-p 8080:8080 \
-v /home/lighthouse/config/quarkus/application.properties:/application/config/application.properties \
bolingcavalry/quarkus-virual-threads-demo:x64-0.0.3
  • 上述命令中,quarkus应用的镜像bolingcavalry/quarkus-virual-threads-demo:x64-0.0.3是我提前准备好的,本篇只管使用即可,至于如何制作此镜像,接下来的文章会有详细说明
  • 用sudo docker logs quarkus命令查看启动日志,如果启动成功,信息如下所示
代码语言:javascript
复制
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2022-10-16 11:46:41,406 INFO  [io.quarkus] (main) quarkus-virual-threads-demo 1.0-SNAPSHOT on JVM (powered by Quarkus 2.13.2.Final) started in 0.804s. Listening on: http://0.0.0.0:8080
2022-10-16 11:46:41,414 INFO  [io.quarkus] (main) Profile prod activated. 
2022-10-16 11:46:41,414 INFO  [io.quarkus] (main) Installed features: [cdi, reactive-pg-client, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, vertx]

验证基本功能

  • 假设quarkus所在宿主机的IP地址是192.168.0.1,那么在浏览器上访问地址http://192.168.0.1:8080/vt/persons/1,如下图,应用成功从数据库取得数据,并且将当前线程信息也返回给浏览器
image-20221016195054009
image-20221016195054009
  • 作为对比,该服务器上还有个使用虚拟线程的常规web服务,地址是:http://192.168.0.1:8080/pool/persons/1,如下图,该接口也能从数据库取得数据,并且将当前线程信息也返回给浏览器,注意两个接口线程信息的差异
image-20221016195523863
image-20221016195523863
  • 对于虚拟线程的接口http://192.168.0.1:8080/vt/persons/1,可以尝试多次访问,可见每次返回的虚拟线程Id都不一样,而使用线程池的接口http://192.168.0.1:8080/pool/persons/1,多次访问,返回的始终是同一个线程的id
  • 基本功能似乎没有问题,接下来可以压测了,用数据说话

压测使用虚拟线程的接口

  • 这次使用的压测工具名为k6,选它的原因只有两个字:简单
  • 在执行压测的电脑上,确保docker可以正常使用
  • 新建名为k6-vt-docker.js的文件,内容如下,可见非常简单,发请求再校验响应,只要返回码是200,并且body大小大于0就算一次成功,要注意的事两个参数,其中vus是并发数,duration是测试持续时间,可见这里设置的是模拟30用户并发请求,持续时间是60秒
代码语言:javascript
复制
import http from 'k6/http';
import { sleep, check } from 'k6';

export let options = {
  vus: 30,
  duration: '60s',
};

export default function () {
  let r = Math.floor(Math.random() * 6) + 1;
  const res = http.get(`http://192.168.0.1:8080/vt/persons/${r}`);
  check(res, {
    'is status 200': (res) => res.status === 200,
    'body size is > 0': (r) => r.body.length > 0,
  });
  sleep(1);
}
  • 一行命令即可开始压测,如下
代码语言:javascript
复制
 docker run --rm -i loadimpact/k6 run - < k6-vt-docker.js
  • 等待一分钟,压测完成,得到以下数据
image-20221016201343176
image-20221016201343176
  • 这里将几个重要参数介绍一下
  1. checks : 通过率,如果满足返回码200且包体大小大于0的条件,就表示本次请求成功,这里显示100.00%,表示全部通过
  2. http_req_waiting:等待响应的事件,这里avg=33.83ms,表示平均等待时间为33.83毫秒
  3. http_reqs:每秒处理请求数,即常说的QPS,这里是28个每秒
  • 接下来将并发数调整为300(即vue参数),如下图
image-20221016201904998
image-20221016201904998
  • 再次压测,得到结果如下,并发数涨了10倍,QPS也涨了10倍,很完美的线程提升,不过平均等待时间略涨
image-20221016202227598
image-20221016202227598
  • 再来个狠的,并发数一口暴涨到5000试试,如下图,这么高的并发,已经无法保障100%的成功率了,好在95%也不低,另外平均等待时间从39毫秒暴涨到6.26秒,至于QPS当然不会太高,仅比300并发的时候高了百分之五十
image-20221016202751348
image-20221016202751348

压测使用线程池的接口

  • 操作步骤和刚才差不多,只是要修改脚本中的接口地址,如下所示
代码语言:javascript
复制
import http from 'k6/http';
import { sleep, check } from 'k6';

export let options = {
  vus: 30,
  duration: '60s',
};

export default function () {
  let r = Math.floor(Math.random() * 6) + 1;
  const res = http.get(`http://192.168.0.1:8080/pool/persons/${r}`);
  check(res, {
    'is status 200': (res) => res.status === 200,
    'body size is > 0': (r) => r.body.length > 0,
  });
  sleep(1);
}
  • 30并发的压测结果如下,和使用虚拟线程并无区别
image-20221016203908998
image-20221016203908998
  • 300并发压测结果如下,和使用虚拟线程并无区别
image-20221016204237577
image-20221016204237577
  • 5000并发压测结果如下,和虚拟线程相比,平均等待时间略长,QPS略低,但是整体上差别不大
image-20221016204550432
image-20221016204550432

小结

  • 在响应式web服务中,并且关联的数据库操作也是响应式的,相比传统的线程池模型,虚拟线程并未带来明显收益
  • 不甘心啊,接下来就换成SpringBoot应用,模拟咱们日常开发最常见的数据库访问场景,看看相比之下,差距有多大?

停止quarkus应用

  • 由于打算将SpringBoot应用部署在quarkus应用所在电脑上,所以要先停掉quarkus应用,执行以下命令
代码语言:javascript
复制
sudo docker stop quarkus

启动SpringBoot应用

  • 新建名为application.properties的配置文件,内容是数据库相关的参数,如下所示
代码语言:javascript
复制
spring.datasource.url=jdbc:postgresql://42.193.162.141:5432/quarkus_test
spring.datasource.username=quarkus
spring.datasource.password=123456
spring.datasource.driver-class-name=org.postgresql.Driver

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.datasource.hikari.maximum-pool-size=256
  • 我这里,application.properties的路径是:/home/lighthouse/config/springboot/application.properties
  • 检查application.properties文件的可读性,执行以下命令设置
代码语言:javascript
复制
sudo chmod a+r application.properties
  • 执行以下命令即可部署和启动springboot应用,注意application.properties文件的位置要换成您自己的配置文件路径
代码语言:javascript
复制
docker run -idt \
--name springboot \
-p 8080:8080 \
-v /home/lighthouse/config/springboot/application.properties:/application/BOOT-INF/classes/application.properties \
bolingcavalry/springboot-postgresql-demo:x64-0.0.3
  • 上述命令中,springboot应用的镜像bolingcavalry/springboot-postgresql-demo:x64-0.0.3是我提前准备好的,本篇只管使用即可,至于如何制作此镜像,接下来的文章会有详细说明
  • 看到以下日志,证明应用启动成功
代码语言:javascript
复制
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.4)

2022-10-16 13:01:01.022  INFO 1 --- [           main] .b.s.SpringbootPostgresqlDemoApplication : Starting SpringbootPostgresqlDemoApplication v0.0.1-SNAPSHOT using Java 11.0.13 on 5c25db81639e with PID 1 (/application/BOOT-INF/classes started by root in /application)
2022-10-16 13:01:01.025  INFO 1 --- [           main] .b.s.SpringbootPostgresqlDemoApplication : No active profile set, falling back to 1 default profile: "default"
2022-10-16 13:01:01.795  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2022-10-16 13:01:01.857  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 47 ms. Found 1 JPA repository interfaces.
2022-10-16 13:01:02.392  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-10-16 13:01:02.405  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-10-16 13:01:02.405  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.65]
2022-10-16 13:01:02.492  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-10-16 13:01:02.492  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1364 ms
2022-10-16 13:01:02.701  INFO 1 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2022-10-16 13:01:02.741  INFO 1 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.6.11.Final
2022-10-16 13:01:02.867  INFO 1 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2022-10-16 13:01:02.942  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2022-10-16 13:01:03.164  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2022-10-16 13:01:03.179  INFO 1 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
2022-10-16 13:01:03.688  INFO 1 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2022-10-16 13:01:03.695  INFO 1 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
  • 验证服务是否成功,访问:http://192.168.0.1:8080/springboot/persons/1 ,如下图,功能正常
image-20221016210323753
image-20221016210323753

压测SpringBoot应用

  • 压测SpringBoot应用的k6脚本如下
代码语言:javascript
复制
import http from 'k6/http';
import { sleep, check } from 'k6';

export let options = {
  vus: 30,
  duration: '60s',
};

export default function () {
  let r = Math.floor(Math.random() * 6) + 1;
  const res = http.get(`http://192.168.0.1:8080/springboot/persons/${r}`);
  check(res, {
    'is status 200': (res) => res.status === 200,
    'body size is > 0': (r) => r.body.length > 0,
  });
  sleep(1);
}
  • 30并发压测结果如下,这么整齐,和quarkus的基本一致
image-20221016211101018
image-20221016211101018
  • 300并发压测结果如下,几个关键指标依旧和quarkus保持对齐,这让我觉得很索然无味
image-20221016211351254
image-20221016211351254
  • 5000并发,终于有了点差距,但也不明显,成功率91%,低于quarkus的95%,相比quarkus,SpringBoot的平均等待时间略高,QPS略低
image-20221016215029375
image-20221016215029375
  • 至此,咱们通过实际对比的方式,已体验过quarkus使用虚拟线程的web服务,同在quarkus框架中,虚拟线程带来的提升并不明显,在对比未做优化的SpringBoot应用时,也没有在吞吐量方面带来提升,但这一切都不能立即下定论,也许是欣宸的用法有严重问题…如果真有问题,聪明的您,在下一篇文章《编码实战》中一定会发现,如果真有问题,不要可怜我,怼我,让我忏悔…
  • 接下来的文章,咱们一起深入编程实战,使用JDK19后,对JDK、maven、IDE、编译构建、制作docker镜像等方方面面相比以前都有变化,这些都会在文中逐一处理,咱们一起开发出使用虚拟线程的quarkus应

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 欢迎访问我的GitHub
  • 关于虚拟线程
  • 关于支持虚拟线程的框架
  • 本篇概览
  • 环境信息
  • 部署数据库
  • 建表,添加数据
  • 部署quarkus应用
  • 验证基本功能
  • 压测使用虚拟线程的接口
  • 压测使用线程池的接口
  • 小结
  • 停止quarkus应用
  • 启动SpringBoot应用
  • 压测SpringBoot应用
  • 你不孤单,欣宸原创一路相伴
相关产品与服务
容器镜像服务
容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档