Funqy Google Cloud Functions
本指南将介绍快速入门代码,向你展示如何将 Funqy 函数部署到 Google Cloud Functions。
The guide walks through quickstart code to show you how you can deploy Funqy functions to Google Cloud Functions. Unresolved directive in funqy-gcp-functions.adoc - include::{includes}/extension-status.adoc[]
Prerequisites
Unresolved directive in funqy-gcp-functions.adoc - include::{includes}/prerequisites.adoc[]* A Google Cloud Account免费帐号有用。* Cloud SDK CLI Installed
Unresolved directive in funqy-gcp-functions.adoc - include::{includes}/prerequisites.adoc[] * A Google Cloud Account. Free accounts work. * Cloud SDK CLI Installed
Login to Google Cloud
部署应用程序需要登录 Google Cloud。可以按以下方式进行操作:
Login to Google Cloud is necessary for deploying the application. It can be done as follows:
gcloud auth login
The Quickstart
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
Clone the Git repository: git clone {quickstarts-clone-url}
, or download an {quickstarts-archive-url}[archive].
该解决方案位于 funqy-google-cloud-functions-quickstart
directory 中。
The solution is located in the funqy-google-cloud-functions-quickstart
directory.
Creating the Maven Deployment Project
使用 quarkus-funqy-google-cloud-functions
扩展创建应用程序。你可以使用以下 Maven 命令创建它:
Create an application with the quarkus-funqy-google-cloud-functions
extension.
You can use the following Maven command to create it:
Unresolved directive in funqy-gcp-functions.adoc - include::{includes}/devtools/create-app.adoc[]
The Code
代码没有任何特别之处,更重要的是没有什么与 Google Cloud 相关的内容。Funqy 函数可以部署到许多环境,Google Cloud Functions 就是其中之一。
There is nothing special about the code and more importantly nothing Google Cloud specific. Funqy functions can be deployed to many environments and Google Cloud Functions is one of them.
Choose Your Function
每个 Google Cloud Functions 部署只能导出一个 Funqy 函数。如果你在项目中只有一个使用 @Funq
进行注释的方法,那么不必担心。如果你在项目中定义了多个函数,那么你将需要在你 Quarkus application.properties
中选择该函数:
Only one Funqy function can be exported per Google Cloud Functions deployment. If you only have one method
annotated with @Funq
in your project, then there is no worries. If you have multiple functions defined
within your project, then you will need to choose the function within your Quarkus application.properties
:
quarkus.funqy.export=greet
或者,你可以在使用 gcloud
cli 创建 Google Cloud Function 时设置 QUARKUS_FUNQY_EXPORT
环境变量。
Alternatively, you can set the QUARKUS_FUNQY_EXPORT
environment variable when you create the Google Cloud Function using the gcloud
cli.
Build and Deploy
构建项目:
Build the project:
Unresolved directive in funqy-gcp-functions.adoc - include::{includes}/devtools/build.adoc[]
这将编译和打包你的代码。
This will compile and package your code.
Creating the functions
在此示例中,我们将创建两个后台函数和一个云事件函数。后台函数允许你对 Google Cloud 事件做出反应,如 PubSub 信息、Cloud Storage 事件、Firestore 事件……云事件函数允许你使用云事件规范对受支持的事件做出反应。
In this example, we will create two background functions and a cloud events function. Background functions allow you to react to Google Cloud events like PubSub messages, Cloud Storage events, Firestore events, … Cloud events functions allow you to react to supported events using the Cloud Events specification.
Quarkus 支持 Cloud Functions gen 1 和 gen 2。有关 Cloud Functions gen 2 的概述,请参阅 Google Cloud Functions 文档上的 this page。要使用 gen 2,您必须添加 |
Quarkus supports Cloud Functions gen 1 and gen 2. For an overview of Cloud Functions gen 2 see this page on the Google Cloud Functions documentation. To use gen 2 you must add the |
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 响应类型。 |
Function return type can also be Mutiny reactive types. |
-
Injection works inside your function.
-
This is a background function that takes as parameter a
io.quarkus.funqy.gcp.functions.event.PubsubMessage
, this is a convenient class to deserialize a PubSub message. -
This is a background function that takes as parameter a
io.quarkus.funqy.gcp.functions.event.StorageEvent
, this is a convenient class to deserialize a Google Storage event. -
This is a cloud events function, that takes as parameter a
io.cloudevents.CloudEvent
, inside it thegetData()
method will return the event content, a storage event in this case.
我们在 |
we provide convenience class to deserialize common Google Cloud events inside the |
由于我们的项目包含多个函数,我们需要在 application.properties
中通过以下属性指定需要部署的函数:
As our project contains multiple functions, we need to specify which function needs to be deployed via the following property inside our application.properties
:
quarkus.funqy.export=helloPubSubWorld
Build and Deploy to Google Cloud
要构建应用程序,您可以通过 mvn clean package
打包应用程序。您将在 target/deployment
仓库中拥有一个 JAR,其中包含您的类及其所有依赖。
To build your application, you can package your application via mvn clean package
.
You will have a single JAR inside the target/deployment
repository that contains your classes and all your dependencies in it.
然后,您将可以使用 gcloud
将您的函数部署到 Google Cloud。gcloud
命令将根据触发函数的事件而有所不同。
Then you will be able to use gcloud
to deploy your function to Google Cloud.
The gcloud
command will be different depending on which event triggers your function.
我们将使用 Java 17 运行时,但你可以通过在部署命令中使用 |
We will use the Java 17 runtime but you can switch to the Java 21 runtime by using |
首次启动 gcloud functions deploy
时,您可能收到以下错误消息:
The first time you launch the gcloud functions deploy
, you can have the following error message:
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 尚未激活。要解决此错误,请打开错误中显示的网址,按照说明操作,然后等待几分钟再重试该命令。
This means that Cloud Build is not activated yet. To overcome this error, open the URL shown in the error, follow the instructions and then wait a few minutes before retrying the command.
Background Functions - PubSub
使用此命令部署到 Google Cloud Functions:
Use this command to deploy to 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。
The entry point always needs to be io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction
as it will be this class
that will bootstrap Quarkus.
--trigger-resource
选项定义 PubSub 主题的名称,而 --trigger-event google.pubsub.topic.publish
选项定义此函数将由主题中所有消息发布触发。
The --trigger-resource
option defines the name of the PubSub topic, and the --trigger-event google.pubsub.topic.publish
option
define that this function will be triggered by all message publication inside the topic.
要针对此函数触发一个事件,您可以使用 gcloud functions call
命令:
To trigger an event to this function, you can use the gcloud functions call
command:
gcloud functions call quarkus-example-funky-pubsub --data '{"data":"Pub/Sub"}'
--data '{"data":"Hello, Pub/Sub"}'
选项允许您指定要发送到 PubSub 的消息。
The --data '{"data":"Hello, Pub/Sub"}'
option allows you to specify the message to be sent to PubSub.
Background Functions - Cloud Storage
在部署函数之前,你需要创建一个存储存储区。
Before deploying your function, you need to create a bucket.
gsutil mb gs://quarkus-hello
然后,使用此命令部署到 Google Cloud Functions:
Then, use this command to deploy to 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。
The entry point always needs to be io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction
as it will be this class
that will bootstrap Quarkus.
--trigger-resource
选项定义 Cloud Storage 存储分区名称,而 --trigger-event google.storage.object.finalize
选项定义此函数将由此存储分区中的所有新文件触发。
The --trigger-resource
option defines the name of the Cloud Storage bucket, and the --trigger-event google.storage.object.finalize
option
define that this function will be triggered by all new file inside this bucket.
要针对此函数触发一个事件,您可以使用 gcloud functions call
命令:
To trigger an event to this function, you can use the gcloud functions call
command:
gcloud functions call quarkus-example-funky-storage --data '{"name":"test.txt"}'
--data '{"name":"test.txt"}'
选项允许指定伪造的文件名,将为此名称创建伪造的 Cloud Storage 事件。
The --data '{"name":"test.txt"}'
option allow to specify a fake file name, a fake Cloud Storage event will be created for this name.
您也可以使用命令行或 Web 控制台将文件添加到 Cloud Storage。
You can also simply add a file to Cloud Storage using the command line of the web console.
Cloud Events Functions - Cloud Storage
Cloud Events 函数仅是 Cloud Functions gen 2 的一项功能。
Cloud Events Function is a feature of Cloud Functions gen 2 only.
在部署函数之前,你需要创建一个存储存储区。
Before deploying your function, you need to create a bucket.
gsutil mb gs://quarkus-hello
然后,使用此命令部署到 Google Cloud Functions:
Then, use this command to deploy to 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。
The entry point always needs to be io.quarkus.funqy.gcp.functions.FunqyCloudEventsFunction
as it will be this class
that will bootstrap Quarkus.
@1 选项定义了 Cloud Storage 存储分区名称。
The --trigger-bucket=
option defines the name of the Cloud Storage bucket.
要触发事件,你可以向 GCS `example-cloud-event`存储存储区发送一个文件。
To trigger the event, you can send a file to the GCS example-cloud-event
bucket.
Running locally
本地运行函数的最简单方法是使用 Cloud Function Invoker JAR。
The easiest way to locally run your function is using the Cloud Function invoker JAR.
你可以使用以下命令通过 Maven 下载它:
You can download it via Maven using the following command:
mvn dependency:copy \
-Dartifact='com.google.cloud.functions.invoker:java-function-invoker:{gcf-invoker-version}' \
-DoutputDirectory=.
使用 invoker 之前,您首先需要通过以下方式构建您的函数:
Before using the invoker, you first need to build your function via:
Unresolved directive in funqy-gcp-functions.adoc - include::{includes}/devtools/build.adoc[]
然后,你可以用它在本地启动你的函数,同样,该命令取决于函数的类型和事件的类型。
Then you can use it to launch your function locally, again, the command depends on the type of function and the type of events.
Background Functions - PubSub
对于基于时间的函数,你可以使用目标类 @2 启动调用程序。
For background functions, you launch the invoker with a target class of io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction
.
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 相关类。
The --classpath
parameter needs to be set to the previously packaged JAR that contains your function class and all Quarkus related classes.
然后,您可以通过 HTTP 调用(其包含事件的有效负载)来调用您的 background 函数:
Then you can call your background function via an HTTP call with a payload containing the event:
curl localhost:8080 -d '{"data":{"data":"world"}}'
这会使用 PubSubMessage @3 使用一个 PubSub 背景函数调用你的 PubSub 背景函数。
This will call your PubSub background function with a PubSubMessage {"data":"hello"}
.
Background Functions - Cloud Storage
对于基于时间的函数,你可以使用目标类 @2 启动调用程序。
For background functions, you launch the invoker with a target class of io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction
.
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 相关类。
The --classpath
parameter needs to be set to the previously packaged JAR that contains your function class and all Quarkus related classes.
然后,您可以通过 HTTP 调用(其包含事件的有效负载)来调用您的 background 函数:
Then you can call your background function via an HTTP call with a payload containing the event:
curl localhost:8080 -d '{"data":{"name":"text"}}'
这会使用一个 Cloud Storage 事件 @4 调用你的 PubSub 背景函数,也就是一个 @5 文件的事件。
This will call your PubSub background function with a Cloud Storage event {"name":"file.txt"}
, so an event on the file.txt
file.
Cloud Events Functions - Cloud Storage
Cloud Events 函数仅是 Cloud Functions gen 2 的一项功能。
Cloud Events Function is a feature of Cloud Functions gen 2 only.
对于基于云的事件函数,你可以使用目标类 @6 启动调用程序。
For cloud events functions, you launch the invoker with a target class of io.quarkus.funqy.gcp.functions.FunqyCloudEventsFunction`
.
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 相关类。
The --classpath
parameter needs to be set to the previously packaged JAR that contains your function class and all Quarkus related classes.
然后,您可以通过 HTTP 调用(其包含事件的有效负载)来调用您的 background 函数:
Then you can call your background function via an HTTP call with a payload containing the event:
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`文件上的事件调用您的云事件函数。
This will call your cloud events function with an event on the "MY_FILE.txt
file.
Testing your function
Quarkus 借助 @7 依赖项提供内置支持,可用来测试你的 Funqy Google Cloud 函数。
Quarkus provides built-in support for testing your Funqy Google Cloud functions via the quarkus-test-google-cloud-functions
dependency.
要使用它,您必须在 `pom.xml`中添加以下测试依赖项。
To use it, you must add the following test dependency in your pom.xml
.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-google-cloud-functions</artifactId>
<scope>test</scope>
</dependency>
此扩展提供了 `@WithFunction`注释,该注释可用于注释 `@QuarkusTest`测试用例,以便在测试用例之前启动 Cloud Function invoker 并在此类结束之后停止它。必须使用您想要启动的函数的类型来配置此注释,还可以选择函数的名称(如果您在应用程序中有多个函数)。
This extension provides a @WithFunction
annotation that can be used to annotate @QuarkusTest
test cases to start a Cloud Function invoker before you test cases and stop it at the end.
This annotation must be configured with the type of the function you want to launch, and optionally the name of the function in case you have multiple functions inside your application.
将尊重默认 Quarkus 测试端口配置 (quarkus.http.test-port
),如果您将它设置为 0,则会将一个随机端口分配给函数 invoker。
The default Quarkus test port configuration (quarkus.http.test-port
) will be honored and if you set it to 0 a random port will be assigned to the function 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);
}
}
-
This is a standard Quarkus test that must be annotated by
@QuarkusTest
. -
@WithFunction(FunctionType.FUNQY_BACKGROUND)
indicates to launch the function as a Funqy background function. If multiple functions exist in the same application, thefunctionName
attribute must be used to denote which one should be launched. -
REST-assured is used to test the function,
{"data":"world"}
will be sent to it via the invoker.
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);
}
}
-
This is a standard Quarkus test that must be annotated by
@QuarkusTest
. -
@WithFunction(FunctionType.FUNQY_BACKGROUND)
indicates to launch the function as a Funqy background function. If multiple functions exist in the same application, thefunctionName
attribute must be used to denote which one should be launched. -
REST-assured is used to test the function,
{"name":"hello.txt"}
will be sent to it via the invoker.
Cloud Events Functions - Cloud Storage
Cloud Events 函数仅是 Cloud Functions gen 2 的一项功能。
Cloud Events Function is a feature of Cloud Functions gen 2 only.
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);
}
}
-
This is a standard Quarkus test that must be annotated by
@QuarkusTest
. -
@WithFunction(FunctionType.FUNQY_CLOUD_EVENTS)
indicates to launch the function as a Funqy cloud events function. If multiple functions exist in the same application, thefunctionName
attribute must be used to denote which one should be launched. -
REST-assured is used to test the function, this payload that describe a storage event will be sent to it via the invoker.
-
The cloud events headers must be sent via HTTP headers.
What’s next?
如果您正在为 Google Cloud Functions 寻找 Jakarta REST、Servlet 或 Vert.x 支持,我们已经使用Google Cloud Functions HTTP binding实现了该支持。
If you are looking for Jakarta REST, Servlet or Vert.x support for Google Cloud Functions, we have it thanks to our Google Cloud Functions HTTP binding.