编辑此页面

Funqy Google Cloud Functions

本指南将引导您通过快速入门代码,向您展示如何将 Funqy 函数部署到 Google Cloud Functions。

此技术被认为是预览版。

预览版中,不保证向后兼容性和在生态系统中的存在。 具体改进可能需要更改配置或 API,成为稳定版的计划正在进行中。 欢迎通过我们的邮件列表或在我们的 GitHub 问题跟踪器中提出反馈。

有关可能的完整状态列表,请查看我们的常见问题解答条目

先决条件

要完成本指南,您需要

登录 Google Cloud

登录 Google Cloud 对于部署应用程序是必要的。 可以按如下方式完成

gcloud auth login

快速入门

克隆 Git 存储库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载存档

解决方案位于 funqy-google-cloud-functions-quickstart 目录中。

创建 Maven 部署项目

使用 quarkus-funqy-google-cloud-functions 扩展创建一个应用程序。 您可以使用以下 Maven 命令来创建它

CLI
quarkus create app org.acme:funqy-google-cloud-functions \
    --extension='funqy-google-cloud-functions' \
    --no-code
cd funqy-google-cloud-functions

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=funqy-google-cloud-functions \
    -Dextensions='funqy-google-cloud-functions' \
    -DnoCode
cd funqy-google-cloud-functions

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

对于 Windows 用户

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

  • 如果使用 Powershell,请将 -D 参数用双引号括起来,例如 "-DprojectArtifactId=funqy-google-cloud-functions"

代码

代码没有什么特别之处,更重要的是,没有 Google Cloud 特有的东西。 Funqy 函数可以部署到许多环境,Google Cloud Functions 是其中之一。

选择您的函数

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

quarkus.funqy.export=greet

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

构建和部署

构建项目

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

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

创建函数

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

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

构建和部署到 Google Cloud

要构建您的应用程序,您可以通过 mvn clean package 打包您的应用程序。 您将在 target/deployment 存储库中获得一个包含您的类和所有依赖项的单个 JAR。

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

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

首次启动 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 尚未激活。 要克服此错误,请打开错误中显示的 URL,按照说明进行操作,然后等待几分钟再重试该命令。

后台函数 - PubSub

使用此命令部署到 Google Cloud Functions

gcloud functions deploy quarkus-example-funky-pubsub \
  --entry-point=io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction \
  --runtime=java21 --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 的消息。

后台函数 - 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=java21 --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 Storage

在部署您的函数之前,您需要创建一个存储桶。

gsutil mb gs://quarkus-hello

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

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

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

--trigger-bucket= 选项定义 Cloud Storage 存储桶的名称。

要触发事件,您可以将文件发送到 GCS example-cloud-event 存储桶。

本地运行

在本地运行您的函数的最简单方法是使用 Cloud Function 调用器 JAR。

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

mvn dependency:copy \
    -Dartifact='com.google.cloud.functions.invoker:java-function-invoker:1.4.1' \
    -DoutputDirectory=.

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

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

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

后台函数 - PubSub

对于后台函数,您可以使用 io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction 的目标类启动调用器。

java -jar java-function-invoker-1.4.1.jar \
  --classpath target/funqy-google-cloud-functions-1.0.0-SNAPSHOT-runner.jar \
  --target io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction
--classpath 参数需要设置为先前打包的 JAR,其中包含您的函数类和所有 Quarkus 相关类。

然后,您可以通过包含事件有效负载的 HTTP 调用来调用您的后台函数

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

这将使用 PubSubMessage {"data":"hello"} 调用您的 PubSub 后台函数。

后台函数 - Cloud Storage

对于后台函数,您可以使用 io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction 的目标类启动调用器。

java -jar java-function-invoker-1.4.1.jar \
  --classpath target/funqy-google-cloud-functions-1.0.0-SNAPSHOT-runner.jar \
  --target io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction
--classpath 参数需要设置为先前打包的 JAR,其中包含您的函数类和所有 Quarkus 相关类。

然后,您可以通过包含事件有效负载的 HTTP 调用来调用您的后台函数

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

这将使用 Cloud Storage 事件 {"name":"file.txt"} 调用您的 PubSub 后台函数,因此是关于 file.txt 文件的事件。

云事件函数 - Cloud Storage

对于云事件函数,您可以使用 io.quarkus.funqy.gcp.functions.FunqyCloudEventsFunction` 的目标类启动调用器。

java -jar java-function-invoker-1.4.1.jar \
  --classpath target/funqy-google-cloud-functions-1.0.0-SNAPSHOT-runner.jar \
  --target io.quarkus.funqy.gcp.functions.FunqyCloudEventsFunction
--classpath 参数需要设置为先前打包的 JAR,其中包含您的函数类和所有 Quarkus 相关类。

然后,您可以通过包含事件有效负载的 HTTP 调用来调用您的后台函数

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 文件上的事件调用您的云事件函数。

测试您的函数

Quarkus 通过 quarkus-test-google-cloud-functions 依赖项为测试您的 Funqy Google Cloud 函数提供内置支持。

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

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

此扩展提供了一个 @WithFunction 注释,可用于注释 @QuarkusTest 测试用例,以在测试用例之前启动 Cloud Function 调用器,并在结束时停止它。 此注释必须配置为您要启动的函数的类型,并且可以选择函数的名称,以防您的应用程序中包含多个函数。

默认的 Quarkus 测试端口配置 (quarkus.http.test-port) 将被遵守,如果您将其设置为 0,则将为函数调用器分配一个随机端口。

后台函数 - 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. 这是一个标准的 Quarkus 测试,必须通过 @QuarkusTest 注释。

  2. @WithFunction(FunctionType.FUNQY_BACKGROUND) 指示将函数作为 Funqy 后台函数启动。 如果同一应用程序中存在多个函数,则必须使用 functionName 属性来表示应启动哪个函数。

  3. REST-assured 用于测试该函数,{"data":"world"} 将通过调用器发送给它。

后台函数 - 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. 这是一个标准的 Quarkus 测试,必须通过 @QuarkusTest 注释。

  2. @WithFunction(FunctionType.FUNQY_BACKGROUND) 指示将函数作为 Funqy 后台函数启动。 如果同一应用程序中存在多个函数,则必须使用 functionName 属性来表示应启动哪个函数。

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

云事件函数 - 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_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. 这是一个标准的 Quarkus 测试,必须通过 @QuarkusTest 注释。

  2. @WithFunction(FunctionType.FUNQY_CLOUD_EVENTS) 指示将函数作为 Funqy 云事件函数启动。 如果同一应用程序中存在多个函数,则必须使用 functionName 属性来表示应启动哪个函数。

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

  4. 云事件标头必须通过 HTTP 标头发送。

下一步是什么?

如果您正在寻找对 Google Cloud Functions 的 Jakarta REST、Servlet 或 Vert.x 支持,我们通过我们的 Google Cloud Functions HTTP 绑定提供了支持。

相关内容