vlambda博客
学习文章列表

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

Developing Your First Application with Quarkus

在本章中,我们将使用我们可用的工具创建我们的第一个 Quarkus 应用程序。正如您将很快看到的,这是一个非常简单的过程,可以从命令行引导,并且不需要您下载任何外部工具。通过使用这个过程,我们将能够将应用程序编译成本机可执行文件,并有确凿的证据证明,当 Quarkus 将 Java 应用程序转换为本机代码时,它的速度和精简程度。

在本章中,我们将介绍以下主题:

  • Using the Quarkus Maven plugin to bootstrap our projects
  • Alternative methods to kick-start your projects (Quarkus CLI)
  • Creating and executing our first Quarkus application
  • Debugging the application from our IDE
  • Testing the application with an extension of the JUnit test framework
  • Turning our application into native code

Getting started with the Quarkus Maven plugin

为了搭建我们的第一个 Quarkus 应用程序,我们将使用 Maven,它是最常用的软件和发布管理工具。它被各种开发人员使用,主要是因为它提供了以下功能:

  • A standard structure for all your projects
  • Centralized and automatic management of dependencies

为方便用户,Maven 以多种格式分发。您可以从 https://maven.apache.org/download.cgi 下载它。

下载 Maven 后,请执行以下操作:

  1. Unzip the distribution archive (for example, apache-maven-3.6.1-bin.zip) to the directory that you want Maven to be installed in (for example, in your $HOME/apache folder):
$ mkdir $HOME/apache
$ unzip $HOME/Downloads/apache-maven-3.6.1-bin.zip -d $HOME/apache 
  1. Add the Maven libraries to your system path, as shown in the following code. This will update the PATH environment variable:
 $ export PATH=$PATH:$HOME/apache/apache-maven-3.6.1/bin
  1. Once you have completed your installation, you need to check whether Maven has been correctly installed or not. Run mvn --version to verify this:
 $ mvn --version
 Apache Maven 3.6.1  
 Maven home: /home/francesco/apache/apache-maven-3.6.1
 Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b13-0.fc29.x86_64/jre
 Default locale: en_US, platform encoding: UTF-8
 OS name: "linux", version: "4.18.16-300.fc29.x86_64", arch: "amd64", family: "unix"

如果您得到前面的输出,那么您刚刚验证了 Maven 已安装在您的系统上。

Launching the Quarkus Maven plugin

现在 Maven 已经设置好了,我们可以通过它的 Maven 插件来引导我们的第一个 Quarkus 应用程序。 Maven 插件提供了一组目标,可以执行这些目标来编译和构建我们的工件或使用某些功能扩展我们的项目。每个插件,就像每个 Maven 组件一样,都基于以下坐标:

  • groupId: The ID of the project's group. This often matches the ID of the package root directory.
  • artifactId: The ID of the artifact. This often matches with the final artifact name.
  • version: The version of the artifact under the specified group.

您可以通过指示 <groupId>:<artifactId> 坐标从命令行引用 Maven 插件。对于 Quarkus, : 组合是 io.quarkus:quarkus-maven-plugin。您可以使用以下命令检查可用目标及其最新版本:

$ mvn -Dplugin=io.quarkus:quarkus-maven-plugin help:describe

您将看到以下输出:

 Name: Quarkus - Maven Plugin
 Description: Build parent to bring in required dependencies
 Group Id: io.quarkus
 Artifact Id: quarkus-maven-plugin
 Version: 1.0.0.Final
 Goal Prefix: quarkus
 
 This plugin has 11 goals:
 
 quarkus:add-extension
   Description: (no description available)
 
 quarkus:add-extensions
   Description: Allow adding an extension to an existing pom.xml file. 
    Because you can add one or several extension in one go, there are 2 
    mojos:
     add-extensions and add-extension. Both supports the extension and
     extensions parameters.
 
 quarkus:analyze-call-tree
   Description: (no description available)
 
 quarkus:build
   Description: (no description available)
 
 quarkus:create
   Description: This goal helps in setting up Quarkus Maven project with
     quarkus-maven-plugin, with sensible defaults
 
 quarkus:create-example-config
   Description: (no description available)
 
 quarkus:dev
   Description: The dev mojo, that runs a quarkus app in a forked process
 
 quarkus:help
   Description: Display help information on quarkus-maven-plugin.
     Call mvn quarkus:help -Ddetail=true -Dgoal=<goal-name> to display parameter
     details.
 
 quarkus:list-extensions
   Description: (no description available)
 
 quarkus:native-image
   Description: (no description available)
 
 quarkus:remote-dev
   Description: The dev mojo, that connects to a remote host

我们第一个应用程序的源代码可以位于本书 GitHub 存储库的 Chapter02/hello-rest 文件夹中。作为参考,我们使用 Maven 插件创建了应用程序并配置了以下一组参数:

$ mvn io.quarkus:quarkus-maven-plugin:1.0.0.Final:create \
 -DprojectGroupId=com.packt.quarkus.Chapter02 \
 -DprojectArtifactId=hello-rest \
 -DclassName="com.packt.quarkus.Chapter02.SimpleRest" \
 -Dpath="/helloworld"

作为上述命令的结果,在 hello-rest 文件夹中生成了以下目录结构:

 ├── mvnw
 ├── mvnw.cmd
 ├── pom.xml
 └── src
     ├── main
     │   ├── docker
     │   │   ├── Dockerfile.jvm
     │   │   └── Dockerfile.native
     │   ├── java
     │   │   └── com
     │   │       └── packt
     │   │           └── quarkus
     │   │               └── Chapter02
     │   │                   └── SimpleRest.java
     │   └── resources
     │       ├── application.properties
     │       └── META-INF
     │           └── resources
     │               └── index.html
     └── test
         └── java
             └── com
                 └── packt
                     └── quarkus
                         └── Chapter02
                             ├── NativeSimpleRestIT.java
                             └── SimpleRestTest.java

在本章的下一节中,我们将学习如何将项目导入 IntelliJ IDEA(不过,在任何 IDE 中,步骤都几乎相同)。现在,让我们继续前面的项目树视图,看看这个项目中包含的文件:

  • A Project Object Model (pom.xml) with the project configuration
  • A sample REST service named SimpleRest.java and a test class for it named SimpleRestTest.java, as well as a wrapper class named NativeSimpleRestIT.java for executing the test against the native executable application
  • A placeholder for the configuration file (application.properties)
  • An index.html file to indicate where we can add static web content
  • A Dockerfile so that we can create a container out of our applications
  • A Maven wrapper file (mvnw/mvnw.cmd) to allow us to execute Maven goals without prior installation of it

pom.xml 文件将被添加到项目的根目录中。在那里,您将找到一个上部 dependencyManagement 部分,它导入 Quarkus 的 Bill Of Materials。这使我们能够自动链接每个 Quarkus 扩展的确切版本。

在 Quarkus 的 1.0.0.Final 版本中,您将引用 artifactId 命名 quarkus-universe-bom,属于 groupId io.quarkus

在这里,还包含 quarkus-maven-plugin 以允许您打包应用程序并生成本机可执行文件:

<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>
<build>
  <plugins>
    <plugin>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-maven-plugin</artifactId>
      <version>${quarkus-plugin.version}</version>
      <executions>
        <execution>
          <goals>
            <goal>build</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
</build>

进入依赖项部分,您将看到唯一添加的运行时依赖项是以下一个,它允许您执行基本的 REST 应用程序:

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-resteasy</artifactId>
</dependency>

RESTEasy 是 JAX-RS 规范的可移植实现,默认包含在 WildFly 应用服务器 ( http://www.wildfly.org)。您可以使用它通过使用无状态通信的标准 HTTP 方法来提供服务的表示。

除了 quarkus-resteasy 之外,您的 pom.xml 文件中还包含一些其他库,用于测试您的应用程序。这将在测试 Quarkus 应用程序部分中更详细地讨论。

To add additional libraries to your project, besides editing the pom.xml file, you can also use add-extension, which can be found in Quarkus' Maven plugin. An example of this is $ mvn quarkus:add-extension -Dextensions="io.quarkus:quarkus-jsonp,io.quarkus:quarkus-smallrye-health".

以下 SimpleRest 类已在 src/main/java/com/packt/quarkus/Chapter02 中为您自动生成:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
 
@Path("/helloworld")
public class SimpleRest {
 
     @GET
     @Produces(MediaType.TEXT_PLAIN)
     public String hello() {
         return "hello";
     }
}

如您所见,这是一个非常简单的 REST 端点,当 /helloworld GET 请求到达默认端口时,它利用 JAX-RS API 生成 TEXT_PLAIN 资源。

Simpler than JAX-RS!
As we mentioned previously, Quarkus simplifies code development to provide sensible defaults. However, we don't need to declare an ApplicationScoped class to bootstrap the REST service anymore since we will get it as the default option.

Running the application

现在,我们准备好运行我们的应用程序了。执行 compilequarkus:dev 目标来构建并运行它:

$ mvn compile quarkus:dev

几秒钟后,应用程序将被编译并执行,如以下日志所示:

[INFO] Scanning for projects...
. . . .
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-rest ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ hello-rest ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /home/francesco/git/packt/Hands-On-Cloud-Native-Applications-with-Java-and-Quarkus/chapter2/hello-rest/target/classes
[INFO] 
[INFO] --- quarkus-maven-plugin:1.0.0.Final:dev (default-cli) @ hello-rest ---
Listening for transport dt_socket at address: 5005
2019-11-11 13:10:34,493 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
2019-11-11 13:10:35,078 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 585ms
2019-11-11 13:10:35,395 INFO  [io.quarkus] (main) Quarkus 1.0.0.CR1 started in 1.079s. Listening on: http://0.0.0.0:8080
2019-11-11 13:10:35,397 INFO  [io.quarkus] (main) Profile dev activated. Live Coding activated.
2019-11-11 13:10:35,397 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

现在,您可以使用浏览器或 curl 等工具请求提供的端点:

 $ curl http://localhost:8080/helloworld
 hello

您可以使用 Ctrl + C 停止应用程序,但我们建议保持它运行,因为我们将很快测试 热重载功能!

Using the Maven plugin to generate a Gradle project

Quarkus Maven 插件与它的名称无关。事实上,你也可以用它来生成Gradle项目。这两种工具之间的比较超出了本书的范围。但是,大量开发人员更喜欢 Gradle 作为构建工具,因为它的建模方式在最基本的方式上可扩展并且具有出色的性能。

话虽如此,您只需将 buildTool 选项设置为 gradle 即可生成 Gradle 项目,否则默认为 maven。以下是使用 Gradle 生成项目的方法:

mvn io.quarkus:quarkus-maven-plugin:1.0.0.Final:create \
 -DprojectGroupId=com.packt.quarkus.Chapter02 \
 -DprojectArtifactId=hello-rest \
 -DclassName="com.packt.quarkus.Chapter02.SimpleRest" \
 -Dpath="/helloworld" \
 -DbuildTool=gradle

生成的 build.gradle 文件定义了一组可用的存储库和依赖项,并设置了核心项目属性,例如 quarkusPlatformGroupIdquarkusPlatformArtifactIdquarkusPlatformVersion 作为变量:

buildscript {
    repositories {
        mavenLocal()
    }
    dependencies {
        classpath "io.quarkus:quarkus-gradle-
        plugin:${quarkusPluginVersion}"
    }
}

plugins {
    id 'java'
}

apply plugin: 'io.quarkus'

repositories {
     mavenLocal()
     mavenCentral()
}

dependencies {
    implementation enforcedPlatform("${quarkusPlatformGroupId}:
    ${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
    implementation 'io.quarkus:quarkus-resteasy'

    testImplementation 'io.quarkus:quarkus-junit5'
    testImplementation 'io.rest-assured:rest-assured'

    nativeTestImplementation 'io.quarkus:quarkus-junit5'
    nativeTestImplementation 'io.rest-assured:rest-assured'
}

group 'org.acme'
version '1.0.0-SNAPSHOT'

compileJava {
    options.compilerArgs << '-parameters'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

前面的所有变量都是从位于项目根目录的 gradle.properties 文件中检索的。

从配置中可以看出,默认项目中还包含一个插件,以便您可以轻松构建应用程序并以开发模式启动它,如下所示:

./gradlew quarkusDev

最后,值得一提的是,Gradle 扩展仍在开发中,因此您可能会在下一个 Quarkus 版本中看到一些更改或更新。

现在,我们将学习如何使用在线 Quarkus 项目生成器轻松引导我们的项目(Maven 或 Gradle)。

Kick-starting applications using the Quarkus online application

引导 Quarkus 应用程序的另一个选项是使用在线应用程序,该应用程序可在以下地址获得:https://code.quarkus .io/

通过登陆该页面,您将能够生成一个带有初始端点的基本项目,以及您在用户界面中签出的所有扩展:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

如前面的屏幕截图所示,默认情况下,仅选择 RESTEasy 扩展。从界面的左上角,您可以配置项目坐标(groupIdartifactId)和构建工具,可以是 Maven 或 Gradle。更多选项可通过 CONFIGURE MORE OPTIONS 面板获得,该面板可让您配置项目的包名称和版本。

通过向下滚动可用扩展列表,您还可以选择尝试使用其他语言(例如 Kotlin 或 Scala)来开发您的 Quarkus 应用程序。这些选项仍在进行中,因此请考虑它们的 API 和/或配置可能会随着扩展的成熟而改变。但是,如果您测试了任何预览扩展,Quarkus 团队非常感谢您的反馈。

完成设置选项后,只需单击 Start a new application 以将工件下载为压缩文件夹:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

现在,您可以将其解压缩并将其导入您喜欢的 IDE。我们将在下一节中执行此操作。

Testing live reload from your IDE

在本节中,我们将使用 Quarkus 的实时重新加载功能。为此,我们将项目导入到我们的 IDE 中,以便我们可以应用一些更改。

导航到 文件 |打开 并指向您创建 Maven 项目的文件夹。它将自动导入您的 IDE。这是 Java 类的 Files 选项卡视图:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

现在,让我们看看实时重载如何与 Quarkus 一起工作。为此,让我们对代码进行一个简单的更改。这里,我们修改了hello方法的返回值,如下:

public class SimpleRest {
 
     @GET
     @Produces(MediaType.TEXT_PLAIN)
     public String hello() {
         return "hello changed!";
     }
 }          

希望您没有停止服务器。现在,尝试再次调用该服务:

$ curl http://localhost:8080/helloworld
 hello changed!

如您所见,在开发模式下运行时,您可以实时重新加载应用程序。很神奇,不是吗?

实时重新加载也适用于资源文件,例如网页或配置属性文件。请求服务会触发对工作空间的扫描,如果检测到任何更改,则会重新编译 Java 文件并重新部署应用程序。然后,重新部署的应用程序会为您的请求提供服务。

Debugging applications

在开发模式下运行时,Quarkus 将自动侦听端口 5005 上的调试器。您可以使用基本的 shell 命令检查调试是否处于活动状态,如下所示:

$ netstat -an | grep 5005
 tcp        0      0 0.0.0.0:5005            0.0.0.0:*               LISTEN     

现在,让我们在 hello 方法中撤消这些更改并包含另一个 hello 方法,该方法接收要检查的参数作为输入:

package com.packt.quarkus.chapter2;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Path("/helloworld")
public class SimpleRest {
    protected final Logger log = LoggerFactory.getLogger(this.getClass());
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/{name}")
    public String hello(@PathParam("name") String name) {

        log.info("Called with "+name);
        return "hello "+name;
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
} 

通过在我们的 REST 服务中使用 @PathParam 表达式,我们将能够在我们的 IDE 中将该表达式的值作为方法变量进行调试。现在,在日志记录语句上放置一个断点,如以下屏幕截图所示:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

接下来,为了将 IntelliJ IDEA 附加到调试器,您必须连接到调试器的端口。在 IntelliJ IDEA 中,您可以通过多种方式执行此操作。最简单的方法是选择 Run |附加到进程。将检测到您的应用程序的可运行进程,如以下屏幕截图所示:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

选择它并检查您是否成功附加到它。您可以从 Debugger Console 执行此操作:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

现在,通过在应用程序末尾添加一个额外的参数来调用应用程序,以便您命中断点:

 $ curl http://localhost:8080/helloworld/frank

在调试器提示符下,您可以从其控制台检查类和方法变量。还可以控制执行路径(Step OverStep IntoStop 等等)通过单击位于 Debugger C 左侧的按钮鞋底,如下:

读书笔记《hands-on-cloud-native-applications-with-java-and-quarkus》使用Quarkus开发您的第一个应用程序

如果您想在启动 Quarkus 应用程序之前等待调试器附加,您可以在命令行上传递 -Ddebug。一旦您的 IDE 调试器连接,Quarkus Augmentor 将启动并执行您的应用程序。另一方面,如果您根本不需要调试器,则可以使用 -Ddebug=false

Testing Quarkus applications

与示例端点一起,Maven 插件自动为我们的 REST 服务包含了一个测试类:

package com.packt.quarkus.chapter2;
 
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
 
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
 
@QuarkusTest
public class SimpleRestTest {
 
     @Test
     public void testHelloEndpoint() {
         given()
           .when().get("/helloworld")
           .then()
              .statusCode(200)
              .body(is("hello"));
     }
 
}

在底层,这个测试类使用 JUnit 作为核心测试框架和 REST Assured 库:

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

REST Assured 是一个 Java 库,可用于使用灵活的领域特定语言 (DSL) 为 REST API 编写强大的测试。 REST Assured 中可用的 Fluent API 支持来自 行为驱动开发 (BDD) 的标准模式及其 Given/When/Then 语法。生成的测试很容易阅读,并且可以包含我们需要的所有步骤,以便只用一行代码构建测试。

现在,我们可以验证响应正文的内容并检查 HTTP 响应状态码是否为 200。我们可以通过运行以下命令来验证测试的执行:

$ mvn clean test

您应该在控制台中看到以下输出:

[INFO] Running com.packt.quarkus.chapter2.SimpleRestTest
 2019-05-16 11:04:21,166 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
 2019-05-16 11:04:21,832 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 669ms
 2019-05-16 11:04:22,108 INFO  [io.quarkus] (main) Quarkus 1.0.0.Final started in 0.265s. Listening on: http://[::]:8081
 2019-05-16 11:04:22,109 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
 [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.958 s - in com.packt.quarkus.chapter2.SimpleRestTest

2019-05-16 11:04:23,263 INFO 

[io.quarkus] (main) Quarkus stopped in 0.005s
 [INFO]
 [INFO] Results:
 [INFO]
 [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
 [INFO]
 [INFO] -----------------------------------------------------------------------
 [INFO] BUILD SUCCESS
 [INFO] -----------------------------------------------------------------------

如您所见,测试在端口 8081 的本地 IP 地址上启动了 Quarkus 运行时。因此,它不会干扰默认运行在端口 8080 上的开发/生产环境。

您可以通过使用 and() 方法连接测试中的多个条件来混合和匹配它们。这种方法就像简单的语法糖一样工作,也就是说,它有助于使代码更具可读性。下面是一个示例,说明如何对标头的 Content-Length 进行检查:

@Test
public void testHelloEndpointHeader() {
    given()
      .when().get("/helloworld")
         .then()
         .statusCode(200)
         .body(is("hello"))
           .and()
            .header("Content-Length","6");
}

通过使用参数化测试,您可以通过提供不同的记录集在一个方法中测试多个场景。 REST Assured 支持两种不同类型的参数:

  • Query parameters: These can be appended at the end of a RESTful API endpoint and are identified by the question mark in front of them. Here's an example:
@Test
public void testHelloEndpointQueryParam() {
    given()
      .param("name","Frank")
      .when().get("/helloworld")
         .then()
         .statusCode(200)
         .body(is("hello"));

}

如您所见,使用查询参数只需要我们通过连接 param() 方法来指定它们的名称和值。

  • Path parameters: These are specified in a similar fashion, that is, by including the pathParam() method with the parameter name/value combination:
@Test
public void testHelloEndpointPathParam() {

 given()
 .pathParam("name", "Frank")
 .when().get("/helloworld/{name}")
 .then()
 .statusCode(200)
 .body(is("hello Frank"));
}

最后,值得一提的是,由于 Quarkus 以最高性能为目标,您还可以根据响应时间来验证您的测试。这可以通过将 time() 连接到您的条件来完成。这是一个将返回响应的时间设置为小于一秒的示例:

@Test
public void testTimedHelloEndpointPathParam() {

 given()
 .pathParam("name", "Frank")
 .when().get("/helloworld/{name}")
 .then()
 .time(lessThan(1000L))
 .body(is("hello Frank"));
}

在本节中,我们介绍了可以使用 REST Assured API 构建的最常见的测试场景。如果您想查看一些更高级的模式,我们建议您查看其 Wiki,该 Wiki 位于 https://github.com/rest-assured/rest-assured/wiki/usage

Choosing a different port for testing

您可以通过在 src/main/resources/application.properties 文件中设置适当的值来更改 Quarkus 用于测试的默认端口 (8081),即Quarkus 的通用配置文件。例如,为了将测试端口转移到 9081,您需要将以下信息添加到 application.properties

quarkus.http.test-port=9081
As an alternative, you can also use the same property at startup by passing the -Dquarkus.http.test-port=9081 flag.

Turning your application into a native executable

现在,是时候检查 Quarkus 可以做什么来将我们的字节码转换为原生可执行文件了。这种魔法是由名为 native 的 Maven 配置文件在后台完成的,当您构建应用程序时,该配置文件开箱即用:

<profile>
  <id>native</id>
  <activation>
    <property>
      <name>native</name>
    </property>
  </activation>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>integration-test</goal>
              <goal>verify</goal>
            </goals>
            <configuration>
              <systemProperties>
                <native.image.path>${project.build.directory}
                  /${project.build.finalName}-runner
                </native.image.path>
              </systemProperties>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <properties>
    <quarkus.package.type>native</quarkus.package.type>
  </properties>
</profile>

此外,由于我们将构建原生镜像的路径设置为系统属性。

在构建可执行文件之前,请确认您已设置 GRAALVM_HOME 在您的环境中,如前一章所述。

接下来,通过执行以下命令创建本机可执行文件:

$ mvn package -Pnative

该插件将开始分析应用程序中使用的类和包装,以及调用树。生成的输出将是一个超级精简的可执行文件,其中仅包含一个薄 JVM 层(足够窄以仅执行应用程序)和应用程序本身。

您应该在输出末尾看到类似于以下内容的内容:

[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 60485ms

连同包含应用程序压缩字节码的 JAR 文件,将在 target 文件夹中生成以下可执行文件:

Nov 11 14:49 hello-rest-1.0-SNAPSHOT-runner
您的应用程序的实际名称可以通过设置 native.image.path 环境变量在您的 pom.xml 文件,即 ${project.build.directory}/${project.build.finalName}-runner 默认。

如您所见,我们有一个大约 20 MB 的可执行应用程序运行时,其中包含所有库以及运行应用程序所需的 JVM 库。您可以使用以下命令执行它:

$ target/hello-rest-1.0-SNAPSHOT-runner

在短短 0.006 秒内,我们就启动并运行了我们的服务。这可以在控制台日志中看到:

2019-11-11 14:53:38,619 INFO  [io.quarkus] (main) hello-rest 1.0-SNAPSHOT (running on Quarkus 1.0.0.CR1) started in 0.014s. Listening on: http://0.0.0.0:8080
2019-11-11 14:53:38,619 INFO  [io.quarkus] (main) Profile prod activated. 
2019-11-11 14:53:38,619 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

让我们通过执行 ps 命令来检查这个紧凑型应用程序的内存使用情况:

$ ps -o pid,rss,command -p $(pgrep -f hello-rest)

这是我从笔记本电脑收集的输出:

PID   RSS   COMMAND
27919 18720 target/hello-rest-1.0-SNAPSHOT-runner

尽管输出可能因您的环境而异,Resident Set Size (RSS) 显示该进程占用了大约 18 MB 的内存,这只是最小值的一小部分Java 应用程序所需的内存大小。

现在,让我们执行它来检查结果:

$ curl http://localhost:8080/helloworld
 hello

如您所见,当我们将应用程序转变为原生应用程序时,结果并没有改变。

Executing integration tests against the native executable

有趣的是,可以测试本机可执行代码。当您生成示例项目时,测试文件夹中包含一个 Native Test 类名称。此类与 Java 测试不同,因为它使用 @NativeImageTest 注释进行注释。

由于 Maven 故障安全插件配置,所有其余部分都以 *IT 结尾或用 @NativeImageTest 将针对本机可执行文件运行。

在代码方面,无需进行任何更改,因为它使用继承从我们的 SimpleRestTest 类执行本机可执行测试:

@NativeImageTest
public class NativeSimpleRestIT extends SimpleRestTest {

    // Execute the same tests but in native mode.
}

verify 目标是测试本机可执行文件所必需的。在此之前,请确保您已将安装 GraalVM 的路径导出到您的环境中:

export GRAALVM_HOME=/path/to/graal

现在,您可以运行 verify 目标来测试本机可执行应用程序:

$ mvn verify -Pnative

检查结果是否与我们在本章前面的测试 Quarkus 应用程序部分中产生的结果相同:

[INFO] Running com.packt.quarkus.chapter2.SimpleRestTest
 2019-05-16 11:35:22,509 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
 2019-05-16 11:35:23,084 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 575ms
 2019-05-16 11:35:23,419 INFO  [io.quarkus] (main) Quarkus 1.0.0.Final started in 0.319s. Listening on: http://[::]:8081
 2019-05-16 11:35:23,419 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
 2019-05-16 11:35:24,354 INFO  [com.pac.qua.cha.SimpleRest] (XNIO-1 task-1) Called with Frank
 2019-05-16 11:35:24,598 INFO  [com.pac.qua.cha.SimpleRest] (XNIO-1 task-1) Called with Frank
 [INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.215 s - in com.packt.quarkus.chapter2.SimpleRestTest

伟大的!我们刚刚设法在两种场景(JVM 和本机可执行文件)中测试了我们的示例应用程序。

Summary

在本章中,我们完成了第一个 Quarkus 概念验证项目,该项目是通过 quarkus-maven-plugin 生成的。默认应用程序是 REST 服务的原型,具有所有最小功能和我们逐渐丰富的 Test 类。在本章的第二部分,我们看到了如何使用 quarkus-maven-plugin 的适当本地配置文件将 Java 应用程序代码转换为精简的本地可执行文件。

到目前为止,我们只触及了 Quarkus 可以做的事情的皮毛。现在,是时候继续学习如何从我们的原生应用程序中创建 Container 图像并将其部署到 Kubernetes 环境中了。这就是我们将在下一章讨论的内容。