Funqy Google Cloud Functions

本指南将介绍快速入门代码,向你展示如何将 Funqy 函数部署到 Google Cloud Functions。 :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/extension-status.adoc :keywords: Quarkus, 中文文档, 编程技术

该技术被认为是 {extension-status}。 有关可能状态的完整列表,请查看我们的 FAQ entry.

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

  • 安装了 JDK 17+,已正确配置 JAVA_HOME

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

Login to Google Cloud

部署应用程序需要登录 Google Cloud。可以按以下方式进行操作:

gcloud auth login

The Quickstart

克隆 Git 存储库: git clone $${quickstarts-base-url}.git,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。

该解决方案位于 funqy-google-cloud-functions-quickstart directory 中。

Creating the Maven Deployment Project

使用 quarkus-funqy-google-cloud-functions 扩展创建应用程序。你可以使用以下 Maven 命令创建它:

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

适用于 Windows 用户:

  • 如果使用 cmd,(不要使用反斜杠 \ ,并将所有内容放在同一行上)

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

The Code

代码没有任何特别之处,更重要的是没有什么与 Google Cloud 相关的内容。Funqy 函数可以部署到许多环境,Google Cloud Functions 就是其中之一。

Choose Your Function

每个 Google Cloud Functions 部署只能导出一个 Funqy 函数。如果你在项目中只有一个使用 @Funq 进行注释的方法,那么不必担心。如果你在项目中定义了多个函数,那么你将需要在你 Quarkus application.properties 中选择该函数:

quarkus.funqy.export=greet

或者,你可以在使用 gcloud cli 创建 Google Cloud Function 时设置 QUARKUS_FUNQY_EXPORT 环境变量。

Build and Deploy

构建项目:

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

这将编译和打包你的代码。

Creating the functions

在此示例中,我们将创建两个后台函数和一个云事件函数。后台函数允许你对 Google Cloud 事件做出反应,如 PubSub 信息、Cloud Storage 事件、Firestore 事件……云事件函数允许你使用云事件规范对受支持的事件做出反应。

Quarkus 支持 Cloud Functions gen 1 和 gen 2。有关 Cloud Functions gen 2 的概述,请参阅 Google Cloud Functions 文档上的 this page。要使用 gen 2,您必须添加 --gen2 参数。

import jakarta.inject.Inject;

import io.cloudevents.CloudEvent;
import io.quarkus.funqy.Funq;
import io.quarkus.funqy.gcp.functions.event.PubsubMessage;
import io.quarkus.funqy.gcp.functions.event.StorageEvent;

public class GreetingFunctions {

    @Inject GreetingService service; (1)

    @Funq (2)
    public void helloPubSubWorld(PubsubMessage pubSubEvent) {
        String message = service.hello(pubSubEvent.data);
        System.out.println(pubSubEvent.messageId + " - " + message);
    }

    @Funq (3)
    public void helloGCSWorld(StorageEvent storageEvent) {
        String message = service.hello("world");
        System.out.println(storageEvent.name + " - " + message);
    }

    @Funq (4)
    public void helloCloudEvent(CloudEvent cloudEvent) {
        System.out.println("Receive event Id: " + cloudEvent.getId());
        System.out.println("Receive event Subject: " + cloudEvent.getSubject());
        System.out.println("Receive event Type: " + cloudEvent.getType());
        System.out.println("Receive event Data: " + new String(cloudEvent.getData().toBytes()));
        System.out.println("Be polite, say " + service.hello("world"));
    }
}

函数返回类型还可以是 Mutiny 响应类型。

  1. 注入在您的函数内部运作。

  2. 这是一个将 io.quarkus.funqy.gcp.functions.event.PubsubMessage 作为参数的后台函数,这是一个用来反序列化 PubSub 消息的便捷类。

  3. 这是一个将 io.quarkus.funqy.gcp.functions.event.StorageEvent 作为参数的后台函数,这是一个用来反序列化 Google Storage 事件的便捷类。

  4. 这是一个云事件函数,它将 io.cloudevents.CloudEvent 作为参数,其中 getData() 方法将返回事件内容,在本例中为存储事件。

我们在 io.quarkus.funqy.gcp.functions.event 包中提供便捷类来反序列化常见的 Google Cloud 事件。它们不是必须的,您可以使用所需的任何对象。

由于我们的项目包含多个函数,我们需要在 application.properties 中通过以下属性指定需要部署的函数:

quarkus.funqy.export=helloPubSubWorld

Build and Deploy to Google Cloud

要构建应用程序,您可以通过 mvn clean package 打包应用程序。您将在 target/deployment 仓库中拥有一个 JAR,其中包含您的类及其所有依赖。

然后,您将可以使用 gcloud 将您的函数部署到 Google Cloud。gcloud 命令将根据触发函数的事件而有所不同。

我们将使用 Java 17 运行时,但你可以通过在部署命令中使用 --runtime=java21`来切换到 Java 21 运行时,而不是使用 `--runtime=java17

首次启动 gcloud functions deploy 时,您可能收到以下错误消息:

ERROR: (gcloud.functions.deploy) OperationError: code=7, message=Build Failed: Cloud Build has not been used in project <project_name> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudbuild.googleapis.com/overview?project=<my-project> then retry.

这意味着 Cloud Build 尚未激活。要解决此错误,请打开错误中显示的网址,按照说明操作,然后等待几分钟再重试该命令。

Background Functions - PubSub

使用此命令部署到 Google Cloud Functions:

gcloud functions deploy quarkus-example-funky-pubsub \
  --entry-point=io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction \
  --runtime=java17 --trigger-resource hello_topic --trigger-event google.pubsub.topic.publish \
  --source=target/deployment

入口点始终需要是 io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction ,因为该类将引导 Quarkus。

--trigger-resource 选项定义 PubSub 主题的名称,而 --trigger-event google.pubsub.topic.publish 选项定义此函数将由主题中所有消息发布触发。

要针对此函数触发一个事件,您可以使用 gcloud functions call 命令:

gcloud functions call quarkus-example-funky-pubsub --data '{"data":"Pub/Sub"}'

--data '{"data":"Hello, Pub/Sub"}' 选项允许您指定要发送到 PubSub 的消息。

Background Functions - Cloud Storage

在部署函数之前,你需要创建一个存储存储区。

gsutil mb gs://quarkus-hello

然后,使用此命令部署到 Google Cloud Functions:

gcloud functions deploy quarkus-example-funky-storage \
  --entry-point=io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction \
  --runtime=java17 --trigger-resource quarkus-hello --trigger-event google.storage.object.finalize \
  --source=target/deployment

入口点始终需要是 io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction ,因为该类将引导 Quarkus。

--trigger-resource 选项定义 Cloud Storage 存储分区名称,而 --trigger-event google.storage.object.finalize 选项定义此函数将由此存储分区中的所有新文件触发。

要针对此函数触发一个事件,您可以使用 gcloud functions call 命令:

gcloud functions call quarkus-example-funky-storage --data '{"name":"test.txt"}'

--data '{"name":"test.txt"}' 选项允许指定伪造的文件名,将为此名称创建伪造的 Cloud Storage 事件。

您也可以使用命令行或 Web 控制台将文件添加到 Cloud Storage。

Cloud Events Functions - Cloud Storage

Cloud Events 函数仅是 Cloud Functions gen 2 的一项功能。

在部署函数之前,你需要创建一个存储存储区。

gsutil mb gs://quarkus-hello

然后,使用此命令部署到 Google Cloud Functions:

gcloud functions deploy quarkus-example-cloud-event --gen2 \
  --entry-point=io.quarkus.funqy.gcp.functions.FunqyCloudEventsFunction \
  --runtime=java17 --trigger-bucket=example-cloud-event --source=target/deployment

入口点始终需要是 io.quarkus.funqy.gcp.functions.FunqyCloudEventsFunction ,因为该类将引导 Quarkus。

@1 选项定义了 Cloud Storage 存储分区名称。

要触发事件,你可以向 GCS `example-cloud-event`存储存储区发送一个文件。

Running locally

本地运行函数的最简单方法是使用 Cloud Function Invoker JAR。

你可以使用以下命令通过 Maven 下载它:

mvn dependency:copy \
    -Dartifact='com.google.cloud.functions.invoker:java-function-invoker:{gcf-invoker-version}' \
    -DoutputDirectory=.

使用 invoker 之前,您首先需要通过以下方式构建您的函数:

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

然后,你可以用它在本地启动你的函数,同样,该命令取决于函数的类型和事件的类型。

Background Functions - PubSub

对于基于时间的函数,你可以使用目标类 @2 启动调用程序。

java -jar java-function-invoker-{gcf-invoker-version}.jar \
  --classpath target/funqy-google-cloud-functions-1.0.0-SNAPSHOT-runner.jar \
  --target io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction

`--classpath`参数需要设置为先前打包的 JAR,其中包含您的函数类和所有 Quarkus 相关类。

然后,您可以通过 HTTP 调用(其包含事件的有效负载)来调用您的 background 函数:

curl localhost:8080 -d '{"data":{"data":"world"}}'

这会使用 PubSubMessage @3 使用一个 PubSub 背景函数调用你的 PubSub 背景函数。

Background Functions - Cloud Storage

对于基于时间的函数,你可以使用目标类 @2 启动调用程序。

java -jar java-function-invoker-{gcf-invoker-version}.jar \
  --classpath target/funqy-google-cloud-functions-1.0.0-SNAPSHOT-runner.jar \
  --target io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction

`--classpath`参数需要设置为先前打包的 JAR,其中包含您的函数类和所有 Quarkus 相关类。

然后,您可以通过 HTTP 调用(其包含事件的有效负载)来调用您的 background 函数:

curl localhost:8080 -d '{"data":{"name":"text"}}'

这会使用一个 Cloud Storage 事件 @4 调用你的 PubSub 背景函数,也就是一个 @5 文件的事件。

Cloud Events Functions - Cloud Storage

Cloud Events 函数仅是 Cloud Functions gen 2 的一项功能。

对于基于云的事件函数,你可以使用目标类 @6 启动调用程序。

java -jar java-function-invoker-{gcf-invoker-version}.jar \
  --classpath target/funqy-google-cloud-functions-1.0.0-SNAPSHOT-runner.jar \
  --target io.quarkus.funqy.gcp.functions.FunqyCloudEventsFunction

`--classpath`参数需要设置为先前打包的 JAR,其中包含您的函数类和所有 Quarkus 相关类。

然后,您可以通过 HTTP 调用(其包含事件的有效负载)来调用您的 background 函数:

curl localhost:8080 \
  -X POST \
  -H "Content-Type: application/json" \
  -H "ce-id: 123451234512345" \
  -H "ce-specversion: 1.0" \
  -H "ce-time: 2020-01-02T12:34:56.789Z" \
  -H "ce-type: google.cloud.storage.object.v1.finalized" \
  -H "ce-source: //storage.googleapis.com/projects/_/buckets/MY-BUCKET-NAME" \
  -H "ce-subject: objects/MY_FILE.txt" \
  -d '{
        "bucket": "MY_BUCKET",
        "contentType": "text/plain",
        "kind": "storage#object",
        "md5Hash": "...",
        "metageneration": "1",
        "name": "MY_FILE.txt",
        "size": "352",
        "storageClass": "MULTI_REGIONAL",
        "timeCreated": "2020-04-23T07:38:57.230Z",
        "timeStorageClassUpdated": "2020-04-23T07:38:57.230Z",
        "updated": "2020-04-23T07:38:57.230Z"
      }'

这将使用 `"MY_FILE.txt`文件上的事件调用您的云事件函数。

Testing your function

Quarkus 借助 @7 依赖项提供内置支持,可用来测试你的 Funqy Google Cloud 函数。

要使用它,您必须在 `pom.xml`中添加以下测试依赖项。

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-test-google-cloud-functions</artifactId>
    <scope>test</scope>
</dependency>

此扩展提供了 `@WithFunction`注释,该注释可用于注释 `@QuarkusTest`测试用例,以便在测试用例之前启动 Cloud Function invoker 并在此类结束之后停止它。必须使用您想要启动的函数的类型来配置此注释,还可以选择函数的名称(如果您在应用程序中有多个函数)。

将尊重默认 Quarkus 测试端口配置 (quarkus.http.test-port),如果您将它设置为 0,则会将一个随机端口分配给函数 invoker。

Background Functions - PubSub

import static io.restassured.RestAssured.given;

import org.junit.jupiter.api.Test;

import io.quarkus.google.cloud.functions.test.FunctionType;
import io.quarkus.google.cloud.functions.test.WithFunction;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest (1)
@WithFunction(FunctionType.FUNQY_BACKGROUND) (2)
class GreetingFunctionsPubsubTest {
    @Test
    public void test() {
        given()
                .body("{\"data\":{\"data\":\"world\"}}") (3)
                .when()
                .post()
                .then()
                .statusCode(200);
    }
}
  1. 这是必须由 `@QuarkusTest`注释的标准 Quarkus 测试。

  2. @8 指示将该函数作为 Funqy 背景函数启动。如果同一应用程序中存在多个函数,则必须使用 @9 属性来表示应当启动哪一个。

  3. REST-assured 用于测试函数,将通过调用器将 @10 发送给它。

Background Functions - Cloud Storage

import static io.restassured.RestAssured.given;

import org.junit.jupiter.api.Test;

import io.quarkus.google.cloud.functions.test.FunctionType;
import io.quarkus.google.cloud.functions.test.WithFunction;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest (1)
@WithFunction(FunctionType.FUNQY_BACKGROUND) (2)
class GreetingFunctionsStorageTest {
    @Test
    public void test() {
        given()
                .body("{\"data\":{\"name\":\"hello.txt\"}}") (2)
                .when()
                .post()
                .then()
                .statusCode(200);
    }
}
  1. 这是必须由 `@QuarkusTest`注释的标准 Quarkus 测试。

  2. @8 指示将该函数作为 Funqy 背景函数启动。如果同一应用程序中存在多个函数,则必须使用 @9 属性来表示应当启动哪一个。

  3. REST-assured 用于测试函数,`{"name":"hello.txt"}`将通过调用器发送到该函数。

Cloud Events Functions - Cloud Storage

Cloud Events 函数仅是 Cloud Functions gen 2 的一项功能。

import static io.restassured.RestAssured.given;

import org.junit.jupiter.api.Test;

import io.quarkus.google.cloud.functions.test.FunctionType;
import io.quarkus.google.cloud.functions.test.WithFunction;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest (1)
@WithFunction(FunctionType.FUNQY_CLOUD_EVENTS) (2)
class GreetingFunctionsCloudEventTest {
    @Test
    public void test() {
        given()
                .body("{\n" + (3)
                        "        \"bucket\": \"MY_BUCKET\",\n" +
                        "        \"contentType\": \"text/plain\",\n" +
                        "        \"kind\": \"storage#object\",\n" +
                        "        \"md5Hash\": \"...\",\n" +
                        "        \"metageneration\": \"1\",\n" +
                        "        \"name\": \"MY_FILE.txt\",\n" +
                        "        \"size\": \"352\",\n" +
                        "        \"storageClass\": \"MULTI_REGIONAL\",\n" +
                        "        \"timeCreated\": \"2020-04-23T07:38:57.230Z\",\n" +
                        "        \"timeStorageClassUpdated\": \"2020-04-23T07:38:57.230Z\",\n" +
                        "        \"updated\": \"2020-04-23T07:38:57.230Z\"\n" +
                        "      }")
                .header("ce-id", "123451234512345") (4)
                .header("ce-specversion", "1.0")
                .header("ce-time", "2020-01-02T12:34:56.789Z")
                .header("ce-type", "google.cloud.storage.object.v1.finalized")
                .header("ce-source", "//storage.googleapis.com/projects/_/buckets/MY-BUCKET-NAME")
                .header("ce-subject", "objects/MY_FILE.txt")
                .when()
                .post()
                .then()
                .statusCode(200);
    }
}
  1. 这是必须由 `@QuarkusTest`注释的标准 Quarkus 测试。

  2. @11 表示将该函数作为 Funqy 云事件函数启动。如果同一应用程序中存在多个函数,则必须使用 @12 属性来表示应当启动哪一个。

  3. REST-assured 用于测试函数,通过调用器将描述存储事件的此有效负载发送到该函数。

  4. Cloud Events 标头必须通过 HTTP 标头发送。

What’s next?

如果您正在为 Google Cloud Functions 寻找 Jakarta REST、Servlet 或 Vert.x 支持,我们已经使用Google Cloud Functions HTTP binding实现了该支持。