本页介绍了如何使用 Functions 框架编写 HTTP 和事件驱动型 Cloud Run 函数。
Functions 框架概览
编写函数源代码时,您必须使用 Functions 框架,这是一个用于编写 Cloud Run functions 的开源库。借助 Functions 框架,您可以编写在 Cloud Run 和其他环境(包括本地开发机器和基于 Knative 的环境)中运行的轻量级函数。
借助 Functions 框架,您可以:
- 调用 Cloud Run 函数以响应请求。
- 自动取消封送符合 CloudEvents 规范的事件,������是一���以通用方式描述事件数据的行业标准规范。
- 启动本地开发服务器以实现快速测试。
Functions 框架提供了一个用于构建模块化服务的接口。如需在源代码中使用 Functions 框架,请指定以下内容:
函数入口点
您的源代码必须定义函数入口点,这是 Cloud Run 调用函数时运行的代码。您可以在部署函数时指定此入口点。
如何定义入口点取决于您使用的语言运行时。有些语言使用函数作为入口点,而另一些语言使用类作为入口点。
签名类型
使用 Functions 框架编写函数源代码时,您必须指定以下两种签名类型之一:
- HTTP 函数:注册 HTTP 处理程序函数。如果您的函数需要网址端点并必须响应 HTTP 请求(例如针对 webhook),可使用 HTTP 函数。
- 事件驱动型函数(也称为 CloudEvents 函数):用于注册 CloudEvents 处理程序函数。如果您的函数应直接触发以响应 Google Cloud 项目中的事件(例如 Pub/Sub 主题中收到消息或 Cloud Storage 存储桶发生更改),请使用事件驱动型函数。
源目录结构
许多编程语言都支持 Functions 框架。您选择的语言运行时和您要编写的函数类型决定了如何设计代码结构并实现函数。
为了让 Cloud Run 能够找到函数定义,每个语言运行时都对构建源代码有一定的要求。
Node.js
Node.js 函数的基本目录结构如下:
. ├── index.js └── package.json
默认情况下,Cloud Run 会尝试从函数目录的根目录中名为 index.js 的文件加载源代码。如需指定其他主源文件,请使用 package.json 文件中的 main 字段。
package.json 文件还必须包含 Node.js 版 Functions 框架作为依赖项:
{
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
},
"type": "module"
}
主文件中的代码必须定义函数入口点,并且可以导入其他代码和 Node.js 模块。主文件还可以定义多个函数入口点,可单独进行部署。
如需了解详情,请参阅 Node.js 运行时概览和 Node.js 版 Functions 框架。
Python
Python 函数的基本目录结构如下:
. ├── main.py └── requirements.txt
Cloud Run 会从函数目录的根目录中名为 main.py 的文件加载源代码。您必须将主文件命名为 main.py。
requirements.txt 文件必须包含 Python 版 Functions 框架作为依赖项:
functions-framework==3.*
main.py 文件中的代码必须定义函数入口点,并且可以照常导入其他代码和外部依赖项。main.py 文件还可以定义多个函数入口点,可单独进行部署。
如需了解详情,请参阅 Python 运行时概览和 Python 版 Functions 框架。
Go
Go 函数的基本目录结构如下:
. ├── myfunction.go └── go.mod
您的函数必须位于项目根目录下的 Go 软件包中,软件包及其源文件可以采用任何名称,但函数不能位于 package main 中。如果您需要 main 软件包(例如用于本地测试),则可以在子目录中创建一个软件包:
. ├── myfunction.go ├── go.mod └── cmd/ └── main.go
go.mod 文件必须包含 Go 版 Functions 框架作为依赖项:
module example.com/my-module
require (
github.com/GoogleCloudPlatform/functions-framework-go v1.5.2
)
根软件包中的代码必须定义函数入口点,并且可以照常从子软件包和依赖项导入其他代码。软件包还可以定义多个函数入口点,可单独进行部署。
如需了解详情,请参阅 Go 运行时概览和 Go 版 Functions 框架。
Java
Java 函数的基本目录结构如下:
.
├── pom.xml
└── src/
└── main/
└── java/
└── MyFunction.java
Java 源文件必须位于 src/main/java/ 目录下,并且可以采用任何名称。如果源文件声明了软件包,请在 src/main/java 下添加一个额外的目录,其中包含软件包的名称:
.
├── pom.xml
└── src/
└── main/
└── java/
└── mypackage/
└── MyFunction.java
我们建议将关联测试放在 src/test/java/ 子目录下。
pom.xml 文件必须包含 Java 版 Functions 框架作为依赖项:
...
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.0.4</version>
</dependency>
...
源文件中的代码必须定义函数入口点,并且可以照常导入其他代码和外部依赖项。源文件还可以定义多个函数入口点,可单独进行部署。
如需了解详情,请参阅 Java 运行时概览和 Java 版 Functions 框架。
.NET
.NET 函数的基本目录结构如下:
. ├── MyFunction.cs └── MyProject.csproj
您可以像设计任何其他 .NET 源代码结构一样设计您的项目的结构。源文件可以采用任何名称。
您的项目文件必须包含 .NET 版 Functions 框架作为依赖项:
...
<PackageReference Include="Google.Cloud.Functions.Hosting" Version="1.0.0" />
...
源文件中的代码必须定义函数入口点,并且可以照常导入其他代码和外部依赖项。源文件还可以定义多个函数入口点,可单独进行部署。
如需了解详情,请参阅 .NET 运行时概览和 .NET 版 Functions 框架。
Ruby
Ruby 函数的基本目录结构如下:
. ├── app.rb ├── Gemfile └── Gemfile.lock
Cloud Run 会从函数目录的根目录中名为 app.rb 的文件加载源代码。主文件必须命名为 app.rb。
Gemfile 文件必须包含 Ruby 版 Functions 框架作为依赖项:
source "https://rubygems.org"
gem "functions_framework", "~> 1.0"
app.rb 文件中的代码必须定义函数入口点,并且可以照常导入其他代码和外部依赖项。app.rb 文件还可以定义多个函数入口点,可单独进行部署。
如需了解详情,请参阅 Ruby 运行时概览和 Ruby 版 Functions 框架。
PHP
PHP 函数的基本目录结构如下:
. ├── index.php └── composer.json
Cloud Run 会从函数目录的根目录中名为 index.php 的文件加载源代码。您必须将主文件命名为 index.php。
composer.json 文件必须包含 PHP 版 Functions 框架作为依赖项:
{
"require": {
"google/cloud-functions-framework": "^1.1"
}
}
index.php 文件中的代码必须定义函数入口点,并且可以照常导入其他代码和外部依赖项。index.php 文件还可以定义多个函数入口点,可单独进行部署。
如需了解详情,请参阅 PHP 运行时概览和 PHP 版 Functions 框架。
如果您将多个函数分组到单个项目中,请注意每个函数最终可能共享同一组依赖项。但是,某些函数可能并不需要所有依赖项。
我们建议您尽可能拆分大型多函数代码库,并将每个函数放入各自的顶层目录中(如上例所示),其中包含各自的源文件和项目配置文件。此方法可最大限度地减少特定函数所需的依赖项数量,进而减少函数所需的内存量。
编写 HTTP 函数
如果您要通过 HTTP(S) 请求调用函数,请编写 HTTP 函数。如需支持 HTTP 语义,您可以使用函数框架并指定 HTTP 函数签名来接受特定于 HTTP 的参数。
以下示例展示了每个运行时的基本 HTTP 函数源文件。如需查看完整的有效示例,请参阅使用 Google Cloud CLI 部署 Cloud Run functions 函数。如需详细了解源代码的位置,请参阅源目录结构。
Node.js
ES 模块
import { http } from '@google-cloud/functions-framework';
http('myHttpFunction', (req, res) => {
// Your code here
// Send an HTTP response
res.send('OK');
});
在 package.json 文件中添加以下依赖项,包括 "type": "module":
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
},
"type": "module"
}
CommonJS 模块
const functions = require('@google-cloud/functions-framework');
// Register an HTTP function with the Functions Framework
functions.http('myHttpFunction', (req, res) => {
// Your code here
// Send an HTTP response
res.send('OK');
});
在 package.json 文件中添加以下依赖项:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
}
}
在 Node.js 中,您可以向 Node.js 版 Functions 框架注册 HTTP 处理程序函数。HTTP 处理程序函数必须是 Express 中间件函数,该函数会接受请求和响应参数并发送 HTTP 响应。
Cloud Run 使用 body-parser 根据请求的 Content-Type 标头为您自动解析请求正文,因此您可以在 HTTP 处理程序中访问 req.body 和 req.rawBody 对象。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 myHttpFunction。
Python
import functions_framework
# Register an HTTP function with the Functions Framework
@functions_framework.http
def my_http_function(request):
# Your code here
# Return an HTTP response
return 'OK'
在 Python 中,您可以向 Python 版 Functions 框架注册 HTTP 处理程序函数。HTTP 处理程序函数必须接受 Flask 请求对象作为参数,并返回 Flask 可以将其转换为 HTTP 响应对象的值。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 my_http_function。
Go
package myhttpfunction
import (
"fmt"
"net/http"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
)
func init() {
// Register an HTTP function with the Functions Framework
functions.HTTP("MyHTTPFunction", myHTTPFunction)
}
// Function myHTTPFunction is an HTTP handler
func myHTTPFunction(w http.ResponseWriter, r *http.Request) {
// Your code here
// Send an HTTP response
fmt.Fprintln(w, "OK")
}
在 Go 中,您可以在 init() 函数中向 Go 版 Functions 框架注册 HTTP 处理程序函数。HTTP 处理程序函数必须使用标准 http.HandlerFunc 接口发送 HTTP 响应。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 MyHTTPFunction。
HTTP 处理程序函数必须实现标准 http.HandlerFunc 接口。它接受您的函数用于对请求创建响应的 http.ResponseWriter 接口,以及指向包含入站 HTTP 请求详细信息的 http.Request 结构体的指针。
Java
package myhttpfunction;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
// Define a class that implements the HttpFunction interface
public class MyHttpFunction implements HttpFunction {
// Implement the service() method to handle HTTP requests
@Override
public void service(HttpRequest request, HttpResponse response) throws Exception {
// Your code here
// Send an HTTP response
response.getWriter().write("OK");
}
}
在 Java 中,您需要使用 Functions Framework Java API 通过 HttpFunction 接口实现 HTTP 处理程序类。service() 方法必须发送 HTTP 响应。
函数入口点是 HTTP 处理程序类的完全限定名称,包括软件包名称。在此示例中,入口点为 myhttpfunction.MyHttpFunction。
您的 service 方法会收到描述入站 HTTP 请求的 HttpRequest 对象,以及函数使用响应消息进行填充的 HttpResponse 对象。
.NET
using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace MyProject
{
// Define a class that implements the IHttpFunction interface
public class MyHttpFunction : IHttpFunction
{
// Implement the HandleAsync() method to handle HTTP requests
public async Task HandleAsync(HttpContext context)
{
// Your code here
// Send an HTTP response
await context.Response.WriteAsync("OK");
}
}
}
在 .NET 运行时中,您可以使用 .NET 版 Functions 框架 通过 IHttpFunction 接口实现 HTTP 处理程序类。HandleAsync() 方法接受标准 ASP.NET HttpContext 对象作为参数,并且必须发送 HTTP 响应。
函数入口点是 HTTP 处理程序类的完全限定名称,包括命名空间。在此示例中,入口点为 MyProject.MyHttpFunction。
Ruby
require "functions_framework"
# Register an HTTP function with the Functions Framework
FunctionsFramework.http "my_http_function" do |request|
# Your code here
# Return an HTTP response
"OK"
end
在 Ruby 中,您可以向 Ruby 版 Functions 框架注册 HTTP 处理程序函数。HTTP 处理程序函数必须接受 Rack 请求对象作为参数,并返回可用作 HTTP 响应的值。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 my_http_function。
PHP
<?php
use Google\CloudFunctions\FunctionsFramework;
use Psr\Http\Message\ServerRequestInterface;
// Register an HTTP function with the Functions Framework
FunctionsFramework::http('myHttpFunction', 'myHttpHandler');
// Define your HTTP handler
function myHttpHandler(ServerRequestInterface $request): string
{
// Your code here
// Return an HTTP response
return 'OK';
}
在 PHP 中,您可以向 PHP 版 Functions 框架注册 HTTP 处理程序函数。HTTP 处理程序函数必须接受实现 PSR-7 ServerRequestInterface 接口的参数,并且必须以字符串或对象形式返回实现 PSR-7 ResponseInterface 接口的 HTTP 响应。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 myHttpFunction。
HTTP 请求和响应
向 Functions 框架注册 HTTP 处理程序函数后,您的 HTTP 处理程序可以检查请求方法,并根据该方法执行不同的操作。
当您配置事件提供方以向 Cloud Run 函数发送 HTTP 请求时,您的函数会发送 HTTP 响应。如果函数创���了������������(例如使用线程、future、JavaScript Promise 对象、回调或系统进程),则必须先终止这些任务或以其他方式对其进行解析,然后再发送 HTTP 响应。在 HTTP 响应发送之前未终止的任何任务都可能无法完成,并且可能导致未定义的行为。
处理 CORS
跨域资源共享 (CORS) 的作用是让一个网域上运行的应用访问另一网域中的资源。例如,您可能需要让您的网域向 Cloud Run functions 网域发出请求以访问函数。
如需允许向您的函数发出非同源请求,请在 HTTP 响应中根据需要设置 Access-Control-Allow-Origin 标头。对于预检非同源请求,您必须使用 204 响应代码和其他标头来响应预检 OPTIONS 请求。
Node.js
Python
Go
Java
.NET
Ruby
PHP
如果 CORS ���正确设置,则可能会出现如下错误:
XMLHttpRequest cannot load https://YOUR_FUNCTION_URL. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://YOUR_DOMAIN' is therefore not allowed access.
CORS 限制
对于预检非同源请求,系统会在没有 Authorization 标头的情况下发送预检 OPTIONS 请求,因此所有需要身份验证的 HTTP 函数都会拒绝这些请求。由于预检请求失败,因此主请求也将失败。如需解决此限制,请使用以下选项之一:
- 允许公开访问您的函数。
- 为您的函数配置 Identity-Aware Proxy,以便为发送到 Cloud Run functions 的预检 OPTIONS 请求提供 Authorization 标头。
- 在同一网域上托管您的 Web 应用和 Cloud Run,以避免 CORS。您可以通过将 Firebase Hosting 与 Cloud Run functions 集成来实现此目的。
编写事件驱动型函数
如果您希望函数直接响应 Google Cloud 项目中的事件(例如 Pub/Sub 主题中收到消息或 Cloud Storage 存储分区发生更改)而被触发,可以编写事件驱动型函数。
事件驱动型函数基于 CloudEvents,这是一种用于以通用方式描述事件数据的行业标准规范。如需详细了解 CloudEvents 规范,请查看 CloudEvents GitHub 代码库。CloudEvents 项目还提供一组 CloudEvents SDK,可帮助使用代码中的 CloudEvents 对象。
以下示例展示了每个运行时的事件驱动型函数源文件。如需了解源代码的位置,请参阅源目录结构。
Node.js
ES 模块
import { cloudEvent } from "@google-cloud/functions-framework";
cloudEvent('myCloudEventFunction', cloudEvent => {
// Your code here
// Access the CloudEvent data payload using cloudEvent.data
});
在 package.json 文件中添加以下依赖项,包括 "type": "module":
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
},
"type": "module"
}
CommonJS 模块
const functions = require('@google-cloud/functions-framework');
// Register a CloudEvent function with the Functions Framework
functions.cloudEvent('myCloudEventFunction', cloudEvent => {
// Your code here
// Access the CloudEvent data payload using cloudEvent.data
});
在 package.json 文件中添加以下依赖项:
{
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
}
}
在 Node.js 中,您需要向 Node.js 版 Functions 框架注册 CloudEvent 处理程序函数。您的处理程序函数必须接受 CloudEvent 对象作为参数。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 myCloudEventFunction。
Python
import functions_framework
# Register a CloudEvent function with the Functions Framework
@functions_framework.cloud_event
def my_cloudevent_function(cloud_event):
# Your code here
# Access the CloudEvent data payload via cloud_event.data
在 Python 中,您需要向 Python 版 Functions 框架注册 CloudEvent 处理程序函数。您的处理程序函数必须接受 CloudEvent 对象作为参数。
函数入口点是向 Functions 框架注册的处理程序函数的名称。在此示例中,入口点为 my_cloudevent_function。
Go
package mycloudeventfunction
import (
"context"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"github.com/cloudevents/sdk-go/v2/event"
)
func init() {
// Register a CloudEvent function with the Functions Framework
functions.CloudEvent("MyCloudEventFunction", myCloudEventFunction)
}
// Function myCloudEventFunction accepts and handles a CloudEvent object
func myCloudEventFunction(ctx context.Context, e event.Event) error {
// Your code here
// Access the CloudEvent data payload using e.Data() or e.DataAs(...)
// Returning an error causes its message to be logged.
// Example:
err := myInternalFunction() // may return an error
if err != nil {
// Append error message to log
return err
}
// Return nil if no error occurred
return nil
}
在 Go 中,您需要向 Go 版 Functions 框架注册 CloudEvent 处理程序函数。您的处理程序函数必须接受 CloudEvents event.Event对象作为参数。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 MyCloudEventFunction。
Java
package mycloudeventfunction;
import com.google.cloud.functions.CloudEventsFunction;
import io.cloudevents.CloudEvent;
// Define a class that implements the CloudEventsFunction interface
public class MyCloudEventFunction implements CloudEventsFunction {
// Implement the accept() method to handle CloudEvents
@Override
public void accept(CloudEvent event) {
// Your code here
// Access the CloudEvent data payload using event.getData()
// To get the data payload as a JSON string, use:
// new String(event.getData().toBytes())
}
}
在 Java 中,您需要使用 Functions Framework Java API 通过 CloudEventsFunction 接口实现 CloudEvent 处理程序类。accept() 方法必须接受 CloudEvent 对象作为参数并对事件执行任何处理。
函数入口点是 CloudEvent 处理程序类的完全限定名称,包括软件包名称。在此示例中,入口点为 mycloudeventfunction.MyCloudEventFunction。
.NET
using CloudNative.CloudEvents; using Google.Cloud.Functions.Framework; using System.Threading; using System.Threading.Tasks; namespace MyProject { // Define a class that implements the ICloudEventFunction<T> interface public class MyCloudEventFunction : ICloudEventFunction<CloudEventDataType> { // Implement the HandleAsync() method to handle CloudEvents public Task HandleAsync(CloudEvent cloudEvent, CloudEventDataType data, CancellationToken cancellationToken) { // Your code here // The data argument represents the CloudEvent data payload // Signal function completion return Task.CompletedTask; } } }
在 .NET 运行时中,您需要使用 .NET 版 Functions 框架通过 ICloudEventFunction<T> 接口实现 CloudEvent 处理程序类。HandleAsync() 方法接受 CloudEvent 对象和关联的 CloudEvent 数据载荷作为参数。
CloudEvent 数据载荷参数的���型(在示例代码中显示为 CloudEventDataType)必须与函数处理的事件类型相对应。Google CloudEvents .NET 库为 Google 支持的各种事件提供了数据类型。
函数入口点是 CloudEvent 处理程序类的完全限定名称,包括命名空间。在此示例中,入口点为 MyProject.MyCloudEventFunction。
Ruby
require "functions_framework"
# Register a CloudEvent function with the Functions Framework
FunctionsFramework.cloud_event "my_cloudevent_function" do |cloud_event|
# Your code here
# Access the CloudEvent data payload via cloud_event.data
end
在 Ruby 中,您需要向 Ruby 版 Functions 框架注册 CloudEvent 处理程序函数。您的处理程序函数必须接受 CloudEvents Event 对象作为参数。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 my_cloudevent_function。
PHP
<?php
use CloudEvents\V1\CloudEventInterface;
use Google\CloudFunctions\FunctionsFramework;
// Register a CloudEvent function with the Functions Framework
FunctionsFramework::cloudEvent('myCloudEventFunction', 'myCloudEventHandler');
// Define your CloudEvent handler
function myCloudEventHandler(CloudEventInterface $event): void
{
// Your code here
// Access the CloudEvent data payload using $event->getData()
}
在 PHP 中,您需要向 PHP 版 Functions 框架注册 CloudEvent 处理程序函数。您的处理程序函数必须接受符合 CloudEventInterface 接口的参数。
函数入口点是向 Functions 框架注册处理程序时使用的名称。在此示例中,入口点为 myCloudEventFunction。
对于事件驱动型函数,事件数据会以 CloudEvents 格式传递给函数,CloudEvent 数据载荷将对应于触发该函数的事件类型。如需了解受支持的触发器、事件类型和关联的事件数据格式,请参阅函数触发器。
Google 事件代码库包含用于处理 Google 发出的 CloudEvents 的资源。
函数终止
当函数返回时,Cloud Run 认为事件驱动型函数执行完成。如果函数创建了后台任务(例如使用线程、future、JavaScript Promise 对象、回调或系统进程),您必须先终止这些任务或以其他方式解决这些任务,然后再从函数返回。在函数返回之前未终止的任何任务都可能无法完成,并可能导致未定义的行为。
自动重试
事件驱动型函数可以配置为自动重试失败的调用。如需了解详情,请参阅重试事件驱动型函数。