3.4 验证Quarkus功能
我们采用如下实验环境来验证Quarkus:
·RHEL 7.6
·Quarkus 0.21.2
·OpenShift 3.11
·Graal VM 19.1.1
接下来,我们通过实验环境分别验证:
·编译和部署Quarkus应用
·Quarkus的热加载
·在OpenShift中部署Quarkus应用程序
·Quarkus应用添加REST Client扩展
·Quarkus应用的容错能力
3.4.1 编译和部署Quarkus应用
实验环境是由两个节点(RHEL 7.6)组成的OpenShift集群,如下所示:
[root@master ~]# oc get nodes NAME STATUS ROLES AGE VERSION master.example.com Ready infra,master 339d v1.11.0+d4cacc0 node.example.com Ready compute 339d v1.11.0+d4cacc0
从GitHub上下载Quarkus测试代码,如下所示:
[root@master ~]# git clone https://github.com/redhat-developer-demos/quarkus-tutorial Cloning into 'quarkus-tutorial'... remote: Enumerating objects: 86, done. remote: Counting objects: 100% (86/86), done. remote: Compressing objects: 100% (60/60), done. Receiving objects: 100% (888/888), 1.36 MiB | 73.00 KiB/s, done. remote: Total 888 (delta 44), reused 56 (delta 21), pack-reused 802 Resolving deltas: 100% (439/439), done.
在OpenShift中创建项目quarkustutorial,用于后续部署容器化应用。
[root@master ~]# oc new-project quarkustutorial
设置环境变量,如下所示:
[root@master ~]# cd quarkus-tutorial [root@master quarkus-tutorial]# export TUTORIAL_HOME=`pwd` [root@master quarkus-tutorial]# export QUARKUS_VERSION=0.21.2
在RHEL中创建Quarkus项目,如下所示:
mvn io.quarkus:quarkus-maven-plugin:$QUARKUS_VERSION:create \ -DprojectGroupId="com.example" \ -DprojectArtifactId="fruits-app" \ -DprojectVersion="1.0-SNAPSHOT" \ -DclassName="FruitResource" \ -Dpath="fruit"
创建成功结果如图3-14所示。
图3-14 成功创建Quarkus项目
查看项目中生成的文件,如下所示:
[root@master quarkus-tutorial]# ls -al /root/quarkus-tutorial/work/fruits-app/. total 32 drwxr-xr-x. 4 root root 111 Sep 24 18:12 . drwxr-xr-x. 3 root root 41 Sep 24 18:08 .. -rw-r--r--. 1 root root 53 Sep 24 18:11 .dockerignore -rw-r--r--. 1 root root 295 Sep 24 18:11 .gitignore drwxr-xr-x. 3 root root 21 Sep 24 18:12 .mvn -rwxrwxr-x. 1 root root 10078 Sep 24 18:12 mvnw -rw-rw-r--. 1 root root 6609 Sep 24 18:12 mvnw.cmd -rw-r--r--. 1 root root 3693 Sep 24 18:11 pom.xml drwxr-xr-x. 4 root root 30 Sep 24 18:11 src
我们查看应用的源码,如下所示:
#cat src/main/java/com/example/FruitResource.java package com.example; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/fruit") public class FruitResource { @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return "hello"; } }
上面代码定义了一个名为/fruit的URI,通过get访问时返回hello。
接下来,我们分别通过JVM和Native方式生成并运行Quarkus应用程序。首先通过传统的JVM模式生成应用,编译成功结果如图3-15所示。
./mvnw -DskipTests clean package
图3-15 源码编译成功
查看编译生成的jar文件,如下所示:
[root@node fruits-app]# ls -al target/fruits-app-1.0-SNAPSHOT-runner.jar -rw-r--r--. 1 root root 114363 Sep 24 18:19 target/fruits-app-1.0-SNAPSHOT-runner.jar
接下来,以JVM的方式运行应用,如下所示:
[root@node fruits-app]# java -jar target/fruits-app-1.0-SNAPSHOT-runner.jar 2019-09-24 18:20:29,785 INFO [io.quarkus] (main) Quarkus 0.21.2 started in 1.193s. Listening on: http://[::]:8080 2019-09-24 18:20:29,837 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
应用运行以后,通过浏览器访问应用,可以看到返回值是hello,如图3-16所示。
图3-16 浏览器访问应用
接下来,我们验证Docker-Native的模式来编辑应用,生成二进制文件。在编译的过程会使用Red Hat提供的Docker Image,构建成功后在target目录中生成独立的二进制文件。执行如下命令启动编译:
[root@node fruits-app]# ./mvnw package -DskipTests -Pnative -Dquarkus.native.container-build=true
编译过程如图3-17所示,Quarkus的Docker-Native编译过程会先生成jar文件fruits-app-1.0-SNAPSHOT-runner.jar(这个jar文件和基于JVM方式编译成功的jar文件有所区别)。然后调用Red Hat的容器镜像ubi-quarkus-native-image,从jar文件生成二进制可执行文件fruits-app-1.0-SNAPSHOT-runner。
图3-17 Quarkus Docker-Native编译过程
从fruits-app-1.0-SNAPSHOT-runner.jar文件到二进制构建过程中会嵌入一些库文件(这些库文件是生成fruits-app-1.0-SNAPSHOT-runner.jar文件时产生的),以class的形式存到二进制文件中。lib目录中包含二进制文件fruits-app-1.0-SNAPSHOT-runner运行所需要的内容,如org.graalvm.sdk.graal-sdk-19.2.0.1.jar,如下所示:
[root@node target]# cd fruits-app-1.0-SNAPSHOT-native-image-source-jar [root@node fruits-app-1.0-SNAPSHOT-native-image-source-jar]# ls fruits-app-1.0-SNAPSHOT-runner fruits-app-1.0-SNAPSHOT-runner.jar lib [root@node fruits-app-1.0-SNAPSHOT-native-image-source-jar]# ls lib/* |grep -i gra lib/org.graalvm.sdk.graal-sdk-19.2.0.1.jar
查看生成的二进制文件fruits-app-1.0-SNAPSHOT-runner,直接在RHEL 7中运行,如下所示:
[root@node fruits-app]# ls -al target/fruits-app-1.0-SNAPSHOT-runner -rwxr-xr-x. 1 root root 23092264 Nov 7 22:28 target/fruits-app-1.0-SNAPSHOT-runner [root@node target]# ./fruits-app-1.0-SNAPSHOT-runner 2019-11-08 06:37:13,852 INFO [io.quarkus] (main) fruits-app 1.0-SNAPSHOT (running on Quarkus 0.27.0) started in 0.012s. Listening on: http://0.0.0.0:8080 2019-11-08 06:37:13,852 INFO [io.quarkus] (main) Profile prod activated. 2019-11-08 06:37:13,852 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
通过浏览器访问应用,结果正常,如图3-18所示。
图3-18 应用访问结果
从上面内容我们可以了解到:Quarkus Native的构建环境需要完整的GraalVM环境(RHEL中安装或以容器方式运行),而编译成功的二进制文件已经包含GraalVM的运行时,可以直接在操作系统或容器中直接运行。
生成的二进制文件也可以用容器的方式运行,即构建Docker Image。构建有两种方式:基于传统的JVM和基于Native的方式。
传统JVM模式运行的docker file如下所示,我们可以看到docker file使用的基础镜像是openjdk8,如下所示:
[root@node docker]# cat Dockerfile.jvm FROM fabric8/java-alpine-openjdk8-jre ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" ENV AB_ENABLED=jmx_exporter COPY target/lib/* /deployments/lib/ COPY target/*-runner.jar /deployments/app.jar EXPOSE 8080 # run with user 1001 and be prepared for be running in OpenShift too RUN adduser -G root --no-create-home --disabled-password 1001 \ && chown -R 1001 /deployments \ && chmod -R "g+rwX" /deployments \ && chown -R 1001:root /deployments USER 1001 ENTRYPOINT [ "/deployments/run-java.sh" ]
Native模式运行的docker file如下所示,使用的基础镜像是ubi-minimal。UBI的全称是Universal Base Image,这是Red HatRHEL最轻量级的基础容器镜像,如下所示:
[root@node docker]# cat Dockerfile.native FROM registry.access.redhat.com/ubi8/ubi-minimal WORKDIR /work/ COPY target/*-runner /work/application RUN chmod 775 /work EXPOSE 8080 CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
在构建的时候,推荐使用Dockerfile.native模式构建docker image,构建并运行的命令如下:
[root@node fruits-app]# docker build -f src/main/docker/Dockerfile.native -t example/fruits-app:1.0-SNAPSHOT . && \ > docker run -it --rm -p 8080:8080 example/fruits-app:1.0-SNAPSHOT
命令执行结果如图3-19所示。
图3-19 Native模式构建应用的docker image
查看容器运行情况,可以正常运行,docker image的名称是fruits-app:1.0-SNAPSHOT。
[root@node ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ae46922cd0cf example/fruits-app:1.0-SNAPSHOT "./application -Dq..." 57 seconds ago Up 57 seconds 0.0.0.0:8080->8080/tcp nervous_bartik
至此,我们完成了对Quarkus应用构建和运行的验证。
3.4.2 Quarkus的热加载
接下来,我们验证Quarkus应用在开发模式的热加载功能。以开发模式启动应用后,修改应用源代码无须重新编译和重新运行。如果是Web应用,在前台刷新浏览器即可看到更新结果。Quarkus的开发模式非常适合应用于调试阶段、经常需要调整源码并验证效果的需求。
以开发模式编译并热部署应用,如下所示:
[root@master fruits-app]# ./mvnw compile quarkus:dev [INFO] Scanning for projects... [INFO] [INFO] -----------------------< com.example:fruits-app >----------------------- [INFO] Building fruits-app 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ fruits-app --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 2 resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ fruits-app --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- quarkus-maven-plugin:0.21.2:dev (default-cli) @ fruits-app --- Listening for transport dt_socket at address: 5005 2019-09-24 21:18:06,422 INFO [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation 2019-09-24 21:18:07,572 INFO [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmen-tation completed in 1150ms 2019-09-24 21:18:07,918 INFO [io.quarkus] (main) Quarkus 0.21.2 started in 1.954s. Listening on: http://[::]:8080 2019-09-24 21:18:07,921 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
应用启动成功后,通过浏览器访问效果如图3-20所示。
图3-20 应用访问结果
接下来,修改源码文件src/main/java/com/example/FruitResource.java,将访问返回从hello修改为hello Davidwei!,如下所示:
package com.example; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/fruit") public class FruitResource { @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return "hello Davidwei!"; } }
直接刷新浏览器,如图3-21所示,我们看到浏览的返回与此前在源码中修改的内容一致。
图3-21 应用访问结果
至此,我们完成了对Quarkus应用的热加载功能的验证。
3.4.3 在OpenShift中部署Quarkus应用程序
要将Quarkus应用部署到OpenShift中,首先需要添加Quarkus Kubernetes扩展。
Quarkers的扩展是一组依赖项,可以将它们添加到Quarkus项目中,从而获得特定的功能,例如健康检查等。扩展将配置或引导框架或技术集成到Quarkus应用程序中。通过命令行可以列出Quarkers可用和支持的扩展,如下所示:
[root@master fruits-app]# ./mvnw quarkus:list-extensions [INFO] Scanning for projects... [INFO] [INFO] -----------------------< com.example:fruits-app >----------------------- [INFO] Building fruits-app 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- quarkus-maven-plugin:0.21.2:list-extensions (default-cli) @ fruits-app --- Current Quarkus extensions available: Agroal - Database connection pool quarkus-agroal Amazon DynamoDB quarkus-amazon-dynamodb Apache Kafka Client quarkus-kafka-client Apache Kafka Streams quarkus-kafka-streams Apache Tika quarkus-tika Arc quarkus-arc AWS Lambda quarkus-amazon-lambda Flyway quarkus-flyway Hibernate ORM quarkus-hibernate-orm Hibernate ORM with Panache quarkus-hibernate-orm-panache Hibernate Search + Elasticsearch quarkus-hibernate-search-elasticsearch Hibernate Validator quarkus-hibernate-validator Infinispan Client quarkus-infinispan-client JDBC Driver - H2 quarkus-jdbc-h2 JDBC Driver - MariaDB quarkus-jdbc-mariadb JDBC Driver - PostgreSQL quarkus-jdbc-postgresql Jackson quarkus-jackson JSON-B quarkus-jsonb JSON-P quarkus-jsonp Keycloak quarkus-keycloak Kogito quarkus-kogito Kotlin quarkus-kotlin Kubernetes quarkus-kubernetes Kubernetes Client quarkus-kubernetes-client Mailer quarkus-mailer MongoDB Client quarkus-mongodb-client Narayana JTA - Transaction manager quarkus-narayana-jta Neo4j client quarkus-neo4j Reactive PostgreSQL Client quarkus-reactive-pg-client RESTEasy quarkus-resteasy RESTEasy - JSON-B quarkus-resteasy-jsonb RESTEasy - Jackson quarkus-resteasy-jackson Scheduler quarkus-scheduler Security quarkus-elytron-security Security OAuth2 quarkus-elytron-security-oauth2 SmallRye Context Propagation quarkus-smallrye-context-propagation SmallRye Fault Tolerance quarkus-smallrye-fault-tolerance SmallRye Health quarkus-smallrye-health SmallRye JWT quarkus-smallrye-jwt SmallRye Metrics quarkus-smallrye-metrics SmallRye OpenAPI quarkus-smallrye-openapi SmallRye OpenTracing quarkus-smallrye-opentracing SmallRye Reactive Streams Operators quarkus-smallrye-reactive-streams-operators SmallRye Reactive Type Converters quarkus-smallrye-reactive-type-converters SmallRye Reactive Messaging quarkus-smallrye-reactive-messaging SmallRye Reactive Messaging - Kafka Connector quarkus-smallrye-reactive-messaging-kafka SmallRye Reactive Messaging - AMQP Connector quarkus-smallrye-reactive-messaging-amqp REST Client quarkus-rest-client Spring DI compatibility layer quarkus-spring-di Spring Web compatibility layer quarkus-spring-web Swagger UI quarkus-swagger-ui Undertow quarkus-undertow Undertow WebSockets quarkus-undertow-websockets Eclipse Vert.x quarkus-vertx
添加Quarkus Kubernetes扩展,该扩展使用Dekorate生成默认的Kubernetes资源模板,如下所示:
[root@master fruits-app]# ./mvnw quarkus:add-extension -Dextensions="quarkus-kubernetes" [INFO] Scanning for projects... [INFO] [INFO] -----------------------< com.example:fruits-app >----------------------- [INFO] Building fruits-app 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- quarkus-maven-plugin:0.21.2:add-extension (default-cli) @ fruits-app --- □ Adding extension io.quarkus:quarkus-kubernetes [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.466 s [INFO] Finished at: 2019-09-24T23:27:21-07:00 [INFO] ------------------------------------------------------------------------
配置用于部署到OpenShift的容器和组和名称,将以下属性加到src/main/resources/application.properties,如下所示:
[root@master resources]# cat application.properties quarkus.kubernetes.group=example quarkus.application.name=fruits-app
接下来,运行Maven目标来生成Kubernetes资源,命令执行结果如图3-22所示。
./mvnw package -DskipTests
图3-22 生成Kubernetes资源
接下来,我们检查自动生成的Kubernetes资源,如下所示(这里使用上面步骤中生成的容器镜像fruits-app:1.0-SNAPSHOT):
[root@master fruits-app]# cat target/wiring-classes/META-INF/kubernetes/kubernetes.yml --- apiVersion: "v1" kind: "List" items: - apiVersion: "v1" kind: "Service" metadata: labels: app: "fruits-app" version: "1.0-SNAPSHOT" group: "example" name: "fruits-app" spec: ports: - name: "http" port: 8080 targetPort: 8080 selector: app: "fruits-app" version: "1.0-SNAPSHOT" group: "example" type: "ClusterIP" - apiVersion: "apps/v1" kind: "Deployment" metadata: labels: app: "fruits-app" version: "1.0-SNAPSHOT" group: "example" name: "fruits-app" spec: replicas: 1 selector: matchLabels: app: "fruits-app" version: "1.0-SNAPSHOT" group: "example" template: metadata: labels: app: "fruits-app" version: "1.0-SNAPSHOT" group: "example" spec: containers: - env: - name: "KUBERNETES_NAMESPACE" valueFrom: fieldRef: fieldPath: "metadata.namespace" image: "example/fruits-app:1.0-SNAPSHOT" imagePullPolicy: "IfNotPresent" name: "fruits-app" ports: - containerPort: 8080 name: "http" protocol: "TCP"
在OpenShift中应用Kubernetes资源:
[root@master fruits-app]# oc apply -f target/wiring-classes/META-INF/kubernetes/kubernetes.yml service/fruits-app created deployment.apps/fruits-app created
执行上述命令后,包含应用的Pod会被自动创建,如图3-23所示。
图3-23 查看生成的Pod
在OpenShift中创建路由。
[root@master ~]# oc expose service fruits-app route.route.openshift.io/fruits-app exposed
通过curl验证调用应用fruit URI的返回值,确保应用运行正常:
[root@master ~]# SVC_URL=$(oc get routes fruits-app -o jsonpath='{.spec.host}') [root@master ~]# curl $SVC_URL/fruit Hello DavidWei!
至此,我们成功将Quarkus应用部署到了OpenShift上。
3.4.4 Quarkus应用添加REST Client扩展
在微服务架构中,如果应用要访问外部RESTful Web服务,那么Quarkus需要按照MicroProfile REST Client规范提供REST客户端。
针对fruits-app,我们创建一个可以访问http://www.fruityvice.com的REST客户端,以获取有关水果的营养成分。我们查看RESTful Web服务的页面,通过get方式可以查看所有水果信息,如图3-24所示。
图3-24 通过RESTful Web应用查看所有水果信息
查看香蕉的营养成分,如图3-25所示。
图3-25 通过RESTful Web应用查看香蕉的营养成分
为了让fruits-app应用能够访问RESTful Web应用,我们对其添加REST Client和JSON-B扩展(quarkus-rest-client、quarkus-resteasy-jsonb)。运行以下命令进行添加,执行结果如图3-26所示。
./mvnw quarkus:add-extension -Dextension="quarkus-rest-client, quarkus-resteasy-jsonb"
图3-26 为Quarkus应用添加REST Client和JSON-B扩展
我们还需要创建一个POJO对象,该对象用于将JSON消息从http://www.fruityvice.com反序列化为Java对象。
在src/main/java/com/example中创建名为FruityVice的新Java文件,其内容如下所示:
[root@master example]# cat FruityVice package com.example; public class FruityVice { public static FruityVice EMPTY_FRUIT = new FruityVice(); private String name; private Nutritions nutritions; public String getName() { return name; } public void setName(String name) { this.name = name; } public Nutritions getNutritions() { return nutritions; } public void setNutritions(Nutritions nutritions) { this.nutritions = nutritions; } public static class Nutritions { private double fat; private int calories; public double getFat() { return fat; } public void setFat(double fat) { this.fat = fat; } public int getCalories() { return calories; } public void setCalories(int calories) { this.calories = calories; } } }
接下来创建一个Java接口,该接口充当代码和外部服务之间的客户端。在src/main/java/com/example中创建名为FruityViceService的新Java文件,内容如下所示:
[root@master example]# cat FruityViceService package com.example; import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; @Path("/api") @RegisterRestClient public interface FruityViceService { @GET @Path("/fruit/all") @Produces(MediaType.APPLICATION_JSON) public List<FruityVice> getAllFruits(); @GET @Path("/fruit/{name}") @Produces(MediaType.APPLICATION_JSON) public FruityVice getFruitByName(@PathParam("name") String name); }
配置FruityVice服务,将以下属性添加到src/main/resources/application.properties文件中,如下所示:
[root@master fruits-app]# cat src/main/resources/application.properties quarkus.kubernetes.group=example quarkus.application.name=fruits-app com.example.FruityViceService/mp-rest/url=http://www.fruityvice.com
最后,修改src/main/java/com/example/FruitResource.java,增加FruityViceService的调用,如下所示:
[root@master fruits-app]# cat src/main/java/com/example/FruitResource.java package com.example; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.eclipse.microprofile.rest.client.inject.RestClient; import com.example.FruityViceService; @Path("/fruit") public class FruitResource { @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return "hello"; } @RestClient FruityViceService fruityViceService; @Path("{name}") @GET @Produces(MediaType.APPLICATION_JSON) public FruityVice getFruitInfoByName(@PathParam("name") String name) { return fruityViceService.getFruitByName(name); } }
我们以开发模式启动应用程序,命令执行结果如图3-27所示。
./mvnw compile quarkus:dev
图3-27 以开发模式启动应用
我们通过浏览器访问应用,查看香蕉的营养成分,成功返回信息,如图3-28所示。
图3-28 访问应用查看香蕉的营养成分
至此,我们成功完成了对Quarkus应用添加REST Client扩展的验证。
3.4.5 Quarkus应用的容错能力
在微服务中,容错是非常重要的。在以往的方法中,可以通过微服务治理框架来实现(如Spring Cloud);在Quarkus应用中,Quarkus与MicroProfile Fault Tolerance规范集成提供原生的容错功能。
我们为Quarkus应用程序添加Fault Tolerance扩展(quarkus-smallrye-fault-tolerance),执行如下命令,执行结果如图3-29所示。
./mvnw quarkus:add-extension -Dextension="quarkus-smallrye-fault-tolerance"
图3-29 为Quarkus添加Fault Tolernace扩展
接下来在FruityViceService中添加重试策略。添加org.eclipse.microprofile.fault-tolerance.Retry到源码文件src/main/java/java/com/example/FruityViceService.java中,并添加错误重试的次数和时间(maxRetries=3,delay=2000),如下所示:
package com.example; import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.eclipse.microprofile.faulttolerance.Retry; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; @Path("/api") @RegisterRestClient public interface FruityViceService { @GET @Path("/fruit/all") @Produces(MediaType.APPLICATION_JSON) public List<FruityVice> getAllFruits(); @GET @Path("/fruit/{name}") @Produces(MediaType.APPLICATION_JSON) @Retry(maxRetries = 3, delay = 2000) public FruityVice getFruitByName(@PathParam("name") String name); }
完成配置后,如果访问应用出现任何错误,将自动执行3次重试,重试间隔时间为2秒钟。
接下来,我们以开发模式编译并加载应用。
./mvnw compile quarkus:dev
应用启动后,将实验环境访问外部互联网的连接断掉,并再次对应用发起请求:http://localhost:8080/fruit/banana。在等待大约6秒后(3次重试,每次等待2秒)后,将会发出异常报错,这符合我们的预期,如下所示:
Caused by: javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: java.net.UnknownHostException: www.fruityvice.com Caused by: java.net.UnknownHostException: www.fruityvice.com
有时候,我们并不需要在应用前台报错时显示代码内部内容。出于这个目的,我们修改源FruityViceService,添加org.eclipse.microprofile.faulttolerance.Fallback,使用Micro-Profile的Fallback框架,这样当应用无法访问时,会返回空(return FruityVice.EMPTY_FRUIT;),如下所示:
package com.example; import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.eclipse.microprofile.faulttolerance.ExecutionContext; import org.eclipse.microprofile.faulttolerance.Fallback; import org.eclipse.microprofile.faulttolerance.FallbackHandler; import org.eclipse.microprofile.faulttolerance.Retry; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; @Path("/api") @RegisterRestClient public interface FruityViceService { @GET @Path("/fruit/all") @Produces(MediaType.APPLICATION_JSON) public List<FruityVice> getAllFruits(); @GET @Path("/fruit/{name}") @Produces(MediaType.APPLICATION_JSON) @Retry(maxRetries = 3, delay = 2000) @Fallback(value = FruityViceRecovery.class) public FruityVice getFruitByName(@PathParam("name") String name); public static class FruityViceRecovery implements FallbackHandler<FruityVice> { @Override public FruityVice handle(ExecutionContext context) { return FruityVice.EMPTY_FRUIT; } } }
我们断开对外部互联网的访问,再次访问应用,当超时后会返回空值,如图3-30所示。
图3-30 应用访问返回空值
至此,我们成功完成了对Quarkus应用的容错能力的验证。