导读
Quarkus是一个开源Java框架,它解决了传统框架的弱点,包括大量内存消耗和容器环境的扩展问题。通过Quarkus,Java开发人员可以使用熟悉的技术构建云原生微服务和无服务器功能(serverless functions)。
本文将介绍如何开始使用Quarkus进行serverless function开发,直接跳入构建Java serverless项目。读者还可学习如何优化functions,实现持续测试,制作跨serverless平台的可移植functions等。
介绍
自1995年Java诞生以来,Java框架不断发展以获得更高性能、新特性和对业务应用的更多支持。多年来,企业也采用了多种基础设施——从物理服务器到虚拟机和云环境——运行Java应用程序。
持续可用性是这些应用程序的重中之重;无论工作负载如何,它们都需要稳定、可靠和高效。因此,尽管资源利用率不到50%,但大多数企业仍不得不背负着维护基础设施资源(如CPU、内存、磁盘、网络)的高成本负担。
Serverless(无服务器)架构旨在提供一种有效的解决方案,不论是物理服务器、虚拟机还是云部署,使过度/不足的资源与实际工作负载相一致。
然而,使用Spring Boot等传统Java微服务来实现这些特性和好处是具有挑战性的。在许多情况下,传统的Java框架对于将serverless部署到云端,尤其是在Kubernetes上部署,过于重量级且缓慢。如果Java开发人员可以继续使用相同的Java框架来构建传统的云原生微服务以及新的serverless functions会怎样呢?这种方法减少了新serverless应用框架的学习曲线。
Quarkus是什么?
Quarkus重新思索Java,使用完全封闭的方式来构建和运行它。
它将Java变成了可与Go和Node.js相媲美的运行环境。Quarkus提供近450个集成了企业功能的扩展,如数据管理、serverless无服务器功能、持续测试、web渲染、消息传递、安全性、云部署和业务流程自动化。
Quarkus通过基于容器和Kubernetes的不可变基础设施将Java开发人员带入无服务器功能开发之旅。
如图1所示,通过近乎即时的扩展和类似Go的高密度利用,开发人员能在Kubernetes上部署10倍以上的原生Quarkus应用程序。
Quarkus Java serverless和容器
创建Quarkus Serverless项目
如果还没有创建Kubernetes集群,那么可以使用开源的Minikube, OpenShift Kubernetes Distribution(OKD),或 Red Hat OpenShift。Kubernetes集群基于Kubernetes构建,为开发人员提供全栈自动化操作和自助服务配置。Red Hat OpenShift还允许开发人员创建基于开源Knative的serverless应用程序。本文使用Red Hat OpenShift集群作为部署环境。有关更多信息,可参阅OpenShift集群和Serverless Operator的安装指南。
下面的Maven命令可构建一个新Quarkus项目(quarkus-serverless-examples),其中包含一个简单的RESTful API。此项目还安装quarkus-openshift扩展,用于将Quarkus应用程序部·署到OpenShift群集:
$ mvn io.quarkus:quarkus-maven-plugin:2.2.3.Final:create \-DprojectGroupId=org.acme \-DprojectArtifactId=quarkus-serverless-examples \-Dextensions="openshift" \-DclassName="org.acme.getting.started.GreetingResource"
本地运行Serverless Functions
通常,在Quarkus上开发应用程序的第一步是运行Quarkus开发模式。执行以下Maven命令:
$ ./mvnw quarkus:dev
注:确保持续使用Quarkus开发模式进行实时编码。这能避免在更改代码时重新编译、重新部署应用程序以及重新启动Quarkus运行环境。
持续测试Serverless Functions
在开发serverless function时,必须在代码投入生产前对其进行测试。多年来,专门的质量保证(QA)团队一直在这一阶段发挥重要作用,使用外部持续集成(CI)工具确保业务需求,时至今天依然如此。
不过,Quarkus使开发人员能够在代码运行和更改时自动运行单元测试。Quarkus通过命令行界面(CLI)和开发用户界面(DEV UI)提供这种持续测试特性。打开一个新的web浏览器访问DEV UI (http://localhost:8080/q/dev/)
默认情况下,Quarkus应用程序启动时不运行持续测试。要启动它,单击DEV UI右下角的Tests not running。也可以通过单击DEV UI左侧的Open打开web终端。下图展示了一个示例测试结果。
Quarkus DEV UI——测试通过
使用curl命令调用REST API,也可以通过web浏览器访问端点。输出应该是Hello RESTEasy:
$ curl localhost:8080/hello
Hello RESTEasy
更新src/main/java/GreetingResource.java文件中的hello方法,修改返回的文本:
public String hello() {
return "Welcome, Quarkus Serverless Functions Refcard";
}
再次调用REST API将看到新的输出:
$ curl localhost:8080/hello
Quarkus Serverless Functions Refcard
持续测试会失败,如图所示。要修复它,需更新测试代码中的返回文本与逻辑代码相匹配(Welcome,Quarkus Serverless Functions Refcard)。
Quarkus DEV UI——测试失败
可以重复运行src/test/java/目录中所有测试用例。此特性可确保在开发阶段业务需求开发正确,而无需在测试阶段集成外部CI工具。普通微服务和serverless functions没有太大区别。Quarkus的一个好处是,它使得开发人员能将任何标准的微服务部署成Kubernetes/OpenShift的serverless function。
部署Functions成Knative服务
如果还没有部署过,那就在OpenShift中创建名为quarkus-serverless-examples的项目并部署Quarkus serverless function。要生成Knative和Kubernetes资源,需要在src/main/resources/application.properties中添加以下Quarkus变量:
quarkus.container-image.group=quarkus-serverless-examples <1>
quarkus.container-image.registry=image-registry.openshift-image-registry.svc:5000 <2>
quarkus.kubernetes-client.trust-certs=true <3>
quarkus.kubernetes.deployment-target=knative <4>
quarkus.kubernetes.deploy=true <5>
quarkus.openshift.build-strategy=docker <6>
注:确保使用oc 登录命令登录到OpenShift集群以访问该项目。
以下是对上述六个变量的说明:
<1> 定义部署serverless应用镜像的项目(镜像组)名称。
<2> 定义serverless应用容器镜像推送的容器注册表。
<3> 仅当使用自签名证书时使用(本例中使用它们)。
<4> 生成Knative资源文件(例如,knative.json和knative.yaml).
<5> 容器镜像构建完成后启用Kubernetes/OpenShift部署。
<6> 使用Docker构建策略。
运行以下命令构建serverless应用,然后将其直接部署到OpenShift集群:
$ ./mvnw clean package -DskipTests
输出应以BUILD SUCCESS结尾。使用以下oc命令将Quarkus标签添加到Knative服务,用于在运行的pod中显示Quarkus标志:
$ oc label rev/quarkus-serverless-examples-00001 app.openshift.io/runtime=quarkus --overwrite
访问OpenShift web控制台上Developer perspective菜单中的Topology视图,确认应用程序已部署。Serverless function pod可能会缩小到零(白线圆圈)。
使用以下oc命令检索serverless function的路由URL:
$ oc get rt/quarkus-serverless-examples
NAME URL READY REASON
quarkus-serverless[...] http://quarkus[...].SUBDOMAIN True
使用curl命令访问路由URL:
$ curl http://quarkus-serverless-[...].SUBDOMAIN/hello
将看到与本地时相同的结果。扩展pod需要几秒钟:Welcome, Quarkus Serverless Functions Refcard。
回到OpenShift集群的Topology视图,serverless function(Knative服务)自动扩展,如图所示。
在OpenShift中扩展Quarkus serverless function
注:Knative服务pod将在30秒内再次降为零(默认设置)。
用GraalVM让Serverless Functions运行更快
与传统的云原生Java框架相比,Quarkus使开发人员能构建具有性能优势的本机可执行文件,包括快速启动时间和较小的常驻集(RSS)内存,以实现近乎即时的扩展和高密度的内存利用率。在构建本机可执行文件之前,让我们看看现有的serverless functions启动需要多长时间。访问OpenShift中正在运行的Quarkus pod的日志,将看到作为Knative服务运行着的JVM(HotSpot)serverless function:
...
[io.quarkus] (main) quarkus-serverless-examples 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.1.4.Final) started in 5.984s.
...
Quarkus使用GraalVM构建本机可执行文件。可以选择任何GraalVM发行版,例如Oracle GraalVM社区版(CE)和Mandrel(Oracle GraalVM CE的下游发行版)。Mandrel是为支持在OpenJDK 11上构建Quarkus本机可执行文件。打开pom.xml查看Maven项目的本机配置,使用它来构建本机可执行文件:
<profiles>
<profile>
<id>native</id>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
注:如果尚未在本地安装GraalVM或Mandrel发行版,可使用本地容器引擎(例如Docker)拉取Mandrel容器镜像。这会制成一个在任何支持Linux平台上的本机可执行镜像。
执行下列Maven命令中的一个构建本机可执行镜像。
使用Docker:
$ ./mvnw package -Pnative \ -Dquarkus.native.container-build=true
使用Podman:
$ ./mvnw package -Pnative \ -Dquarkus.native.container-build=true \ -Dquarkus.native.container-runtime=podman
输出应以BUILD SUCCESS结尾。再次访问OpenShift中正在运行的Quarkus pod的日志——将看到作为Knative服务正在运行的native serverless function:
...
[io.quarkus] (main) quarkus-serverless-examples 1.0.0-SNAPSHOT native (powered by Quarkus 2.1.4.Final) started in 0.013s.
...
13毫秒,启动速度快了48倍。请注意,在你的环境中,启动时间可能有所不同。下图比较了Quarkus和传统云原生Java堆栈的扩展速度和内存利用率。
Quarkus性能报告
制作跨Serverless平台的可移植Functions
多云和混合云策略极大地影响了异构平台上应用程序开发和部署的可移植性——从公共托管服务到开源项目。这种趋势也反映在serverless技术栈中,这可能会给开发人员在选择正确的框架和工具方面带来新挑战。
即使在选择了新的serverless开发框架之后,这些挑战仍可能继续存在,因为开发人员可能还需要学习依赖于部署应用程序的serverless平台的其他技术(例如,APIs、CLI工具、SDKs、RBAC策略)。Quarkus通过Funqy扩展解决了这个问题,使开发人员能够在实现serverless function后,无需更改代码即可将function部署到多个serverless运行环境,例如AWS Lambda、Azure Functions、Google Cloud Platform和Knative Events。开发人员可以在学习新serverless技术上花费更少的时间和精力。
为项目添加Quarkus Funqy扩展
运行以下Maven命令添加quarkus-funqy-http扩展以使用Funqy HTTP绑定:
$ ./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-funqy-http"
更新src/main/java/GreetingResource.java文件制作一个更简单但类似的可移植serverless function。如你所见,@Funq注解将hello方法公开为基于Funqy API的serverless function。function名默认情况下就是方法名(hello)。代码行(LOC)也减少了一半:
package org.acme.getting.started;
import io.quarkus.funqy.Funq;
public class GreetingResource {
public String hello() {
return "Welcome, Quarkus Serverless Functions Refcard";
}
}
现在可以在本地开发模式下使用./mvnw quarkus:dev运行此serverless function,也可以开启持续测试。然后可以像前面做的那样,通过./mvnw package -DskipTests将应用程序部署到OpenShift。重新部署后访问端点(/hello)时,将看到新的输出:Welcome, Quarkus ServerlessFunctions Refcard。
部署Quarkus Funqy应用到AWS Lambda
让我们将可移植的serverless function部署到AWS Lambda,它是最受开发人员喜欢的serverless平台之一。首先,需要添加另一个funqy扩展。运行以下两个Maven命令添加funqy扩展quarkus-funqy-amazon-lambda,启用Quarkus中的AWS Lambda部署:
$ ./mvnw quarkus:remove-extension -Dextensions="io.quarkus:quarkus-funqy-http,quarkus-openshift"
$ ./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-funqy-amazon-lambda"
注:由于quarkus-funqy-amazon-lambda扩展也处理HTTP请求绑定,因此移除funqy-http和quarkus-openshift扩展。
更新src/main/java/GreetingResource.java中的hello方法,指定新function名awsfunction,和输出:
"awsfunction")(
public String hello() {
return "Hi, Quarkus Funqy on AWS Lambda";
}
注释掉application.properties中的所有配置行,然后添加以下键值以导出AWS Lambda中的function名:
quarkus.funqy.export=awsfunction
使用以下Maven命令构建serverless function,编译代码并为AWS Lambda部署和本地模拟生成所有必要的资源文件:
$ ./mvnw clean package -DskipTests
在target/目录下应该会生成以下文件:
function.zip–AWS Lambda部署文件
manage.sh–包装AWS CLIs以创建和删除AWS Lambda部署文件的Bash脚本
sam.jvm.yaml–AWS无服务器应用程序模型(SAM)CLI脚本,用于本地测试
sam.native.yaml–带有本地可执行文件的SAM CLI脚本,用于本地测试
使用以下命令部署Quarkus Funqy应用程序到AWS Lambda成为serverless function:
$ LAMBDA_ROLE_ARN=<YOUR_ROLE_ARN> sh target/manage.sh create
注:如果你尚未创建Amazon Resource Names(ARN),可访问链接了解更多信息:https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
输出应以"LastUpdateStatus":"Successful"结尾。
使用凭证访问AWS控制台,然后导航到AWS Lambda服务页面。应该看到Quarkus function已经部署好了,如图所示。
AWS Lambda Service登录页
单击function名QuarkUsServerlesExamples查看Function Overview页面。单击Test选项卡,然后输入空字符串(例如,"")。
输出应该是"Hi, Quarkus Funqy on AWS Lambda"。如果不更改返回文本,则输出应该与之前部署到OpenShift时相同。
AWS Lambda上的测试结果
当不再需要该function为业务特性提供服务时,也可以使用包装脚manage.sh从AWS Lambda上快速删除。在本地环境中使用以下命令从AWS Lambda删除function:
$ LAMBDA_ROLE_ARN=<YOUR_ROLE_ARN> sh target/manage.sh delete
为Quarkus Serverless Functions绑定Knative的CloudEvents
虽然事件可用于服务间通信、触发带外处理或将有效负载发送到Apache Kafka等服务,但开发人员必须花更多的时间和精力以不同的方式处理消息。例如,一些消息实体是JSON格式,而其他应用程序使用二进制格式,如Avro和Protobuf,使用元数据传输有效负载。
CloudEvents提供了一种使用通用格式描述事件的开放规范,提高了跨多个云平台和中间件栈的互操作性(例如,Knative, Debezium, Quarkus)。它还允许开发人员解耦事件生产者和消费者以实现高效的serverless架构。Quarkus funqy扩展能让开发人员为Knative环境中的serverless function绑定CloudEvents用于处理反应式流。这对于那些正在构建通用消息传递格式来描述事件并提高多云和混合云平台之间的互操作性的开发人员来说是有益的。
移除funqy-amazon-lambda扩展,添加quarkus-funqy-knative-events和quarkus-openshift扩展,将Knative的CloudEvent与Quarkus serverless functions进行绑定:
$ ./mvnw quarkus:remove-extension -Dextensions="io.quarkus:quarkus-funqy-amazon-lambda"
$ ./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-funqy-knative-events,quarkus-openshift"
在Quarkus项目中添加一个处理CloudEvent消息的新function(例如,ToLowercaseFunction),如下例所示:
public class ToLowercaseFunction {
("lowercase")
public Output function(Input input, CloudEvent<Input> cloudEvent) {
String inputStr = input.getInput();
String outputStr = Optional.ofNullable(inputStr)
.map(String::toLowerCase)
.orElse("NO DATA");
LOGGER.info("Output CE: {}", outputStr);
return new Output(inputStr, cloudEvent.subject(), outputStr, null);
}
}
注: Input和Output类用于调用本地文件系统上的function方法。可在GitHub repository上找到它们。
取消application.properties中所有配置行的注释,将function名替换为lowercase:
quarkus.funqy.export=lowercase
使用以下Maven命令重新构建和部署function到OpenShift:
$ ./mvnw clean package -DskipTests
向OpenShift中的serverless function发送CloudEvent消息:
$ URL=http://quarkus-serverless-[...].SUBDOMAIN
$ curl -v ${URL} \
-H "Content-Type:application/json" \
-H "Ce-Id:1" \
-H "Ce-Source:quarkus-cloudevent-example" \
-H "Ce-Type:dzone.refcard.quarkus" \
-H "Ce-1.0" \
-d "{\"input\": \"QUARKUS WITH CLOUDEVENT\"}\""
同样,访问OpenShift中正在运行的Quarkus serverless pod的日志,会看到CloudEvent的处理结果(例如,input和output):
...
INFO [org.acm.get.sta.ToLowercaseFunction] (executor-thread-0) Input: Input{input='QUARKUS WITH CLOUDEVENT'}
INFO [org.acm.get.sta.ToLowercaseFunction] (executor-thread-0) Output CE: quarkus with cloudevent
结论
现在需要一种serverless开发模型以满足那些希望按需启动业务应用程序而不是一直运行它们的企业。因为新的云原生部署模型都有容器和Kubernetes,许多企业正在考虑使用Java以外的新编程语言来最大化serverless效率。
参考链接:
https://dzone.com/refcardz/getting-started-with-quarkus-serverless-functions