欢迎访问我的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请求,整体上如下图所示

  1. 由于quarkus对于响应式编程的支持比较完善,因此,quarkus应用中的web处理为响应式,并且数据库驱动也是响应式
  2. 上述基本功能调通后,我们就用压测工具来模拟并发访问,看看使用了虚拟线程的web服务性能究竟如何
  3. 再部署一个同样功能的SpringBoot应用,也做同样的压测,提供参考数据用于对比
  4. 最后画个图说明接下来具体要做的事

环境信息

  • 以下是本次实战的具体环境信息,给您作为参考
  1. 运行环境:一共用到了三台电脑,信息分别如下

  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已经部署好
  • 部署数据库很简单,一行命令即可,如下所示,除了服务,还新建了数据库,并且设置了账号和密码
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条记录
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的配置文件,内容是数据库相关的参数,如下所示
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文件的可读性,执行以下命令设置

sudo chmod a+r application.properties
  • 执行以下命令即可部署和启动quarkus应用,注意application.properties文件的位置要换成您自己的配置文件路径
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命令查看启动日志,如果启动成功,信息如下所示

__  ____  __  _____   ___  __ ____  ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
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,如下图,应用成功从数据库取得数据,并且将当前线程信息也返回给浏览器

  • 作为对比,该服务器上还有个使用虚拟线程的常规web服务,地址是:http://192.168.0.1:8080/pool/persons/1,如下图,该接口也能从数据库取得数据,并且将当前线程信息也返回给浏览器,注意两个接口线程信息的差异

  • 对于虚拟线程的接口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秒
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);
}
  • 一行命令即可开始压测,如下
 docker run --rm -i loadimpact/k6 run - < k6-vt-docker.js
  • 等待一分钟,压测完成,得到以下数据

  • 这里将几个重要参数介绍一下
  1. checks : 通过率,如果满足返回码200且包体大小大于0的条件,就表示本次请求成功,这里显示100.00%,表示全部通过
  2. http_req_waiting:等待响应的事件,这里avg=33.83ms,表示平均等待时间为33.83毫秒
  3. http_reqs:每秒处理请求数,即常说的QPS,这里是28个每秒
  • 接下来将并发数调整为300(即vue参数),如下图

  • 再次压测,得到结果如下,并发数涨了10倍,QPS也涨了10倍,很完美的线程提升,不过平均等待时间略涨

  • 再来个狠的,并发数一口暴涨到5000试试,如下图,这么高的并发,已经无法保障100%的成功率了,好在95%也不低,另外平均等待时间从39毫秒暴涨到6.26秒,至于QPS当然不会太高,仅比300并发的时候高了百分之五十

压测使用线程池的接口

  • 操作步骤和刚才差不多,只是要修改脚本中的接口地址,如下所示
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并发的压测结果如下,和使用虚拟线程并无区别

  • 300并发压测结果如下,和使用虚拟线程并无区别

  • 5000并发压测结果如下,和虚拟线程相比,平均等待时间略长,QPS略低,但是整体上差别不大

小结

  • 在响应式web服务中,并且关联的数据库操作也是响应式的,相比传统的线程池模型,虚拟线程并未带来明显收益

  • 不甘心啊,接下来就换成SpringBoot应用,模拟咱们日常开发最常见的数据库访问场景,看看相比之下,差距有多大?

停止quarkus应用

  • 由于打算将SpringBoot应用部署在quarkus应用所在电脑上,所以要先停掉quarkus应用,执行以下命令
sudo docker stop quarkus

启动SpringBoot应用

  • 新建名为application.properties的配置文件,内容是数据库相关的参数,如下所示
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文件的可读性,执行以下命令设置

sudo chmod a+r application.properties
  • 执行以下命令即可部署和启动springboot应用,注意application.properties文件的位置要换成您自己的配置文件路径
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是我提前准备好的,本篇只管使用即可,至于如何制作此镜像,接下来的文章会有详细说明

  • 看到以下日志,证明应用启动成功


. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: 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'

压测SpringBoot应用

  • 压测SpringBoot应用的k6脚本如下
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的基本一致

  • 300并发压测结果如下,几个关键指标依旧和quarkus保持对齐,这让我觉得很索然无味

  • 5000并发,终于有了点差距,但也不明显,成功率91%,低于quarkus的95%,相比quarkus,SpringBoot的平均等待时间略高,QPS略低

  • 至此,咱们通过实际对比的方式,已体验过quarkus使用虚拟线程的web服务,同在quarkus框架中,虚拟线程带来的提升并不明显,在对比未做优化的SpringBoot应用时,也没有在吞吐量方面带来提升,但这一切都不能立即下定论,也许是欣宸的用法有严重问题...如果真有问题,聪明的您,在下一篇文章《编码实战》中一定会发现,如果真有问题,不要可怜我,怼我,让我忏悔...
  • 接下来的文章,咱们一起深入编程实战,使用JDK19后,对JDK、maven、IDE、编译构建、制作docker镜像等方方面面相比以前都有变化,这些都会在文中逐一处理,咱们一起开发出使用虚拟线程的quarkus应

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

支持JDK19虚拟线程的web框架,之一:体验的更多相关文章

  1. 支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<支持JDK19虚拟线程的web ...

  2. 支持JDK19虚拟线程的web框架之四:看源码,了解quarkus如何支持虚拟线程

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 前文链接 支持JDK19虚拟线程的web框架,之一:体 ...

  3. 支持JDK19虚拟线程的web框架,之五(终篇):兴风作浪的ThreadLocal

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos <支持JDK19虚拟线程的web框架>系列 ...

  4. Java19虚拟线程都来了,我正在写的线程代码会被淘汰掉吗?

    Java19中引入了虚拟线程,虽然默认是关闭的,但是可以以Preview模式启用,这绝对是一个重大的更新,今天Java架构杂谈带大家开箱验货,看看这家伙实现了什么了不起的功能. 1 为什么需要虚拟线程 ...

  5. 150行代码搭建异步非阻塞Web框架

    最近看Tornado源码给了我不少启发,心血来潮决定自己试着只用python标准库来实现一个异步非阻塞web框架.花了点时间感觉还可以,一百多行的代码已经可以撑起一个极简框架了. 一.准备工作 需要的 ...

  6. 两个基于C++/Qt的开源WEB框架

    1.tufao 项目地址: https://github.com/vinipsmaker/tufao 主页: http://vinipsmaker.github.io/tufao/ 介绍: Tufão ...

  7. python web应用--web框架(三)

    了解了WSGI框架,我们发现:其实一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应. 但是如何处理HTTP请求不是问题,问题是如何处理100个不同的URL. 每一个URL ...

  8. polaris: 一个用go实现的支持restful的web框架

    介绍 polaris是一个用go实现的支持restful的web框架,主要参考tornado进行设计. 虽然在go里面搭建一个http server非常的简单,这里强烈推荐gorilla,但并没有很好 ...

  9. Spring与Web框架(例如Spring MVC)漫谈——关于Spring对于多个Web框架的支持

    在看Spring MVC的官方文档时,最后一章是关于Spring对于其它Web框架的支持(如JSF,Apache Struts 2.x,Tapestry 5.x),当然Spring自己的MVC框架Sp ...

随机推荐

  1. 使用Linux、Nginx和Github Actions托管部署ASP.NET Core 6.0应用

    使用Linux.Nginx和Github Actions托管部署ASP.NET Core 6.0应用 前言 本文主要参考微软这篇文档而来 Host ASP.NET Core on Linux with ...

  2. Excel 运算符(一):算术运算符

    算术运算符用于最基本的加.减.乘.除运算. 运算符 含义 实例 结果 + 加法运算 =2+3 5 - 减法运算 =5-2 3 * 乘法运算 =5*2 10 / 除法运算 =4/2 2 % 百分数 =5 ...

  3. [CSP-S 2019 day2 T2] 划分

    题面 题解 CSP赛场上能请教别人吗 在这道题中,我看到了一个很敏感又很熟悉的东西--平方! 这意味着,可以推出一些结论,使这道题几乎可以边输入边解决. 自己在脑子里动态一下就知道,像这种总和一定.代 ...

  4. 随机存取文件流--RandomAccessFile

    * RandomAccessFile的使用* 1.RandomAccessFile直接维承于java.Lang.Object类,实现了DataInputDataOutput接口* 2.RandomAc ...

  5. RabbitMQ 入门系列:6、保障消息:不丢失:发送方、Rabbit存储端、接收方。

    系列目录 RabbitMQ 入门系列:1.MQ的应用场景的选择与RabbitMQ安装. RabbitMQ 入门系列:2.基础含义:链接.通道.队列.交换机. RabbitMQ 入门系列:3.基础含义: ...

  6. C# winfrom ListView控件实现自由设置每一行字体及背景色等

    背景:公司经常会需要将日志信息,输出到一个对话框中显示出来.之前一直采用的listbox控件,操作简单,使用方便,但是遗憾的是,不能自由控制每一行的状态. 于是想了如下几个方案: (1)重绘listb ...

  7. smile——Java机器学习引擎

    资源 https://haifengl.github.io/ https://github.com/haifengl/smile 介绍 Smile(统计机器智能和学习引擎)是一个基于Java和Scal ...

  8. VS Code C++ 代码格式化(clang-format)

    --- # 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto Language: Cpp # BasedOnStyle ...

  9. Fast.Framework ORM 于中秋节后 正式开源

    Fast Framework 作者 Mr-zhong 开源项目地址 https://github.com/China-Mr-zhong/Fast.Framework QQ交流群 954866406 欢 ...

  10. 《Java基础——抽象与接口》

    Java基础--抽象与接口       一.抽象: 规则: 关键字 abstract 修饰的类称为抽象类. 子类通过关键字extends实现继承. 关键字 abstract 修饰的方法称为抽象方法,抽 ...