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

Posted 程序员欣宸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用相关的知识,希望对你有一定的参考价值。

欢迎访问我的GitHub

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

本篇概览

  • 本篇是《支持JDK19虚拟线程的web框架》系列的中篇,前文咱们体验了有虚拟线程支持的web服务,经过测试,发现性能上它与其他两种常见web架构并无明显区别,既然如此,还有必要研究和学习吗?
  • 当然有必要,而且还要通过实战更深入了解虚拟线程与常规线程的区别,在各大框架和库广泛支持虚拟线程之前,打好理论和实践基础,这才是本系列的目标
  • 为了接下来的深入了解,咱们先在本篇打好基础:详细说明前文的web功能是如何开发出来的
  • 为了突出重点,这里先提前剧透,从编码的角度说清楚如何开启虚拟线程支持,其实非常简单,如下图,左侧是quarkus框架下的一个普通web服务,每收到一个web请求,是由线程池中的线程负责响应的,右侧的web服务多了个@RunOnVirtualThread注解,就变成了由新建的虚拟线程去处理web请求,没错,在quarkus框架下使用虚拟线程就是这么简单

  • 在前文中,我们通过返回值也看到了上述两个web服务中,负责web响应的线程的不同,如下所示,从线程名称上很容易看出线程池和虚拟线程的区别

  • 看到这里,您可能会说:就这?一个注解就搞定的事情,你还要写一篇文章?这不是在浪费作者你自己和各位读者的时间吗?

  • 确实,开启虚拟线程,编码只要一行,然而就目前而言,虚拟线程是JDK19专属,而且还只是预览功能,要想在实际运行的时候真正开启并不容易,需要从JDK、maven、IDE等方方面面都要做相关设置,而且如果要做成前文那样的docker镜像,一行docker run命令就能开启虚拟线程,还要在Dockerfile上做点事情(quarkus提供的基础镜像中没有JDK19版本,另外启动命令也要调整)

  • 上述这些都是本文的重点,欣宸已经将这些梳理清楚了,接下来咱们一起实战吧,让前文体验过的web从无到有,再到顺利运行,达到预期

  • 整个开发过程如下图所示,一共十步,接下来开始动手

开发环境

  • 开发电脑:MacBook Pro M1,macOS Monterey 12.6
  • IDE:IntelliJ IDEA 2022.3 EAP (Ultimate Edition) (即未发布前的早期预览版)
  • 另外,M1芯片的电脑上开发和运行JDK19应用,与普通的X86相比感受不到任何变化,只有一点要注意:上传docker镜像到hub.docker.com时,镜像的系统架构是ARM的,这样的镜像在X86电脑上下载下来后不能运行

下载JDK19

  • 下载jdk19,由于电脑是M1芯片,我选择的jdk是azul版本,地址是:https://www.azul.com/downloads/?package=jdk#download-openjdk
  • 使用azul的jdk和之前的oracle版本并无区别,至少在开发环境感受不到,来看下azul官方的说法
  • 实际上,azul的jdk很全面,x86芯片的各平台版本安装包都提供了,您可以根据自己电脑环境选择下载,下面是我选择的适合M1芯片的版本
  • 下载完成后双击安装即可

修改maven的配置

  • 我这里使用的是本地maven,其对应的JDK也要改成19,修改方法是调整环境变量JAVA_HOME,令其指向JDK19目录(在我的电脑上,环境变量是在~/.zshrc里面)

  • 修改后令环境变量生效,然后执行一下命令确认已经使用了JDK19
➜  ~ mvn -version
Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
Maven home: /Users/zhaoqin/software/apache-maven-3.8.5
Java version: 19, vendor: Azul Systems, Inc., runtime: /Library/Java/JavaVirtualMachines/zulu-19.jdk/Contents/Home
Default locale: zh_CN_#Hans, platform encoding: UTF-8
OS name: "mac os x", version: "12.6", arch: "aarch64", family: "mac"

创建Quarkus项目

  • 打开IDEA,新建项目,选择Quarkus项目
  • 接下来选择要用到的扩展包(其实就是在图形化页面添加jar依赖),这里的选择如下图:Reactive PostgreSQL clientRESTEasy Reactive Jackson
  • 点击上图右下角的Create按钮后项目开始创建,稍作等待,项目创建完成,如下图,此刻只能感慨:quarkus太贴心,不但有demo源码,还有各种版本的Dockerfile文件,而且git相关的配置也有,甚至README.md都写得那么详细,我是不是可以点击运行按钮直接把程序run起来了

IDEA设置

  • 由于要用到JDK19,下面几项设置需要检查并确认
  • 首先是Project设置,如下图

  • 其次是Modules设置,先配置Sources这个tab页

  • 接下来是Dependencies这个tab页
  • 进入IDEA系统设置菜单
  • 如下图,三个位置需要设置
  • 设置完成了,接下来开始编码

编码

  • 首先确认pom.xml,这是IDEA帮我们创建的,内容如下,有两处改动稍后会说到
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>quarkus-virual-threads-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <compiler-plugin.version>3.8.1</compiler-plugin.version>
        <maven.compiler.release>19</maven.compiler.release>
        <maven.compiler.source>19</maven.compiler.source>
        <maven.compiler.target>19</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
        <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
        <quarkus.platform.version>2.13.2.Final</quarkus.platform.version>
        <skipITs>true</skipITs>
        <surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>$quarkus.platform.group-id</groupId>
                <artifactId>$quarkus.platform.artifact-id</artifactId>
                <version>$quarkus.platform.version</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-reactive-pg-client</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-arc</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy-reactive</artifactId>
        </dependency>

        <!-- 生成测试数据 -->
        <dependency>
            <groupId>net.datafaker</groupId>
            <artifactId>datafaker</artifactId>
            <version>1.6.0</version>
        </dependency>

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>$quarkus.platform.group-id</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <version>$quarkus.platform.version</version>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                            <goal>generate-code</goal>
                            <goal>generate-code-tests</goal>
                        </goals>
                    </execution>
                </executions>
                <!-- 这里是新增的虚拟线程相关特性,start -->
                <configuration>
                    <source>19</source>
                    <target>19</target>
                    <compilerArgs>
                        <arg>--enable-preview</arg>
                    </compilerArgs>
                    <jvmArgs>--enable-preview --add-opens java.base/java.lang=ALL-UNNAMED</jvmArgs>
                </configuration>
                <!-- 这里是新增的虚拟线程相关特性,end -->
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>$compiler-plugin.version</version>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>$surefire-plugin.version</version>
                <configuration>
                    <systemPropertyVariables>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                        <maven.home>$maven.home</maven.home>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>$surefire-plugin.version</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                        <configuration>
                            <systemPropertyVariables>
                                <native.image.path>$project.build.directory/$project.build.finalName-runner
                                </native.image.path>
                                <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                                <maven.home>$maven.home</maven.home>
                            </systemPropertyVariables>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <profile>
            <id>native</id>
            <activation>
                <property>
                    <name>native</name>
                </property>
            </activation>
            <properties>
                <skipITs>false</skipITs>
                <quarkus.package.type>native</quarkus.package.type>
            </properties>
        </profile>
    </profiles>
</project>

  • pom.xml的第一处改动如下图,要确保全部是19
  • 第二处改动,是在quarkus-maven-plugin插件中增加额外的配置参数,如下图红框
  • 接下来新增配置文件application.properties,在resources目录下
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2

quarkus.datasource.username=quarkus
quarkus.datasource.password=123456
quarkus.datasource.reactive.url=postgresql://192.168.0.1:5432/quarkus_test
  • 开始写java代码了,首先是启动类VirtualThreadsDemoApp.java
package com.bolingcavalry;

import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class VirtualThreadsDemoApp 

    public static void main(String... args) 
        Quarkus.run(args);
    

  • 数据库对应的model类有两个,第一个是gender字段的枚举
package com.bolingcavalry.model;

public enum Gender 
    MALE, FEMALE;

  • 表对应的实体类
package com.bolingcavalry.model;

import io.vertx.mutiny.sqlclient.Row;

public class Person 
    private Long id;
    private String name;
    private int age;
    private Gender gender;
    private Integer externalId;

    public String getThreadInfo() 
        return threadInfo;
    

    public void setThreadInfo(String threadInfo) 
        this.threadInfo = threadInfo;
    

    private String threadInfo;

    public Person() 
    

    public Person(Long id, String name, int age, Gender gender, Integer externalId) 
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.externalId = externalId;
        this.threadInfo = Thread.currentThread().toString();
    

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public int getAge() 
        return age;
    

    public void setAge(int age) 
        this.age = age;
    

    public Gender getGender() 
        return gender;
    

    public void setGender(Gender gender) 
        this.gender = gender;
    

    public Integer getExternalId() 
        return externalId;
    

    public void setExternalId(Integer externalId) 
        this.externalId = externalId;
    

    public static Person from(Row row) 
        return new Person(
                row.getLong("id"),
                row.getString("name"),
                row.getInteger("age"),
                Gender.valueOf(row.getString("gender")),
                row.getInteger("external_id"));
    

  • 接下来是操作数据库的dao类,可见使用操作方式还是很原始的,还要在代码中手写SQL,取出也要逐个字段匹配,其实quarkus也支持JPA,只不过本篇使用的是响应式数据库驱动,所以选用的是Vert.x生成的连接池PgPool
package com.bolingcavalry.repository;

import com.bolingcavalry.model.Person;
import io.vertx.mutiny.pgclient.PgPool;
import io.vertx.mutiny.sqlclient.Row;
import io.vertx.mutiny.sqlclient.RowSet;
import io.vertx.mutiny.sqlclient.Tuple;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@ApplicationScoped
public 以上是关于支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用的主要内容,如果未能解决你的问题,请参考以下文章

支持JDK19虚拟线程的web框架,之一:体验

支持JDK19虚拟线程的web框架,之三:观察运行中的虚拟线程

支持JDK19虚拟线程的web框架,之三:观察运行中的虚拟线程

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

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

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