本頁說明如何使用 Functions Framework 編寫 HTTP 和事件驅動的 Cloud Run 函式。
Functions Framework 總覽
編寫函式原始碼時,您必須使用 Functions Framework,這是用於編寫 Cloud Run 函式的開放原始碼程式庫。透過 Functions Framework,您可以編寫簡易函式,並在 Cloud Run 和其他環境中執行,包括本機開發電腦和以 Knative 為基礎的環境。
Functions Framework 可讓您:
- 呼叫 Cloud Run 函式以回應要求。
- 自動將符合 CloudEvents 規格的事件解除封送。CloudEvents 是業界標準規格,用於以通用方式描述事件資料。
- 啟動本機開發伺服器以進行測試。
Functions Framework 提供介面,可建構模組化服務。如要在原始碼中使用 Functions Framework,請指定下列項目:
函式進入點
原始碼必須定義函式進入點,也就是 Cloud Run 叫用函式時執行的程式碼。部署函式時,您會指定這個進入點。
定義進入點的方式取決於您使用的語言執行階段。有些語言會使用函式做為進入點,有些則會使用類別。
簽名類型
使用 Functions Framework 編寫函式的原始碼時,您必須指定下列其中一種簽章類型:
- HTTP 函式:註冊 HTTP 處理常式函式。如果函式需要網址端點,且必須回應 HTTP 要求 (例如 Webhook),請使用 HTTP 函式。
- 事件導向函式,又稱 CloudEvents 函式:註冊 CloudEvents 處理常式函式。如果函式是直接因應 Google Cloud 專案中的事件而觸發,例如 Pub/Sub 主題上的訊息或 Cloud Storage 值區中的變更,請使用事件驅動函式。
來源目錄結構
Functions Framework 支援多種程式設計語言,您選擇的語言執行階段和想編寫的函式類型,會決定程式碼的結構和函式的實作方式。
為使 Cloud Run 能夠找到函式定義,每個語言執行階段都對原始碼結構有一定的要求。
Node.js
Node.js 函式的基本目錄結構如下:
. ├── index.js └── package.json
根據預設,Cloud Run 會嘗試從函式目錄根層級名為 index.js
的檔案載入原始碼。如要指定其他主要來源檔案,請使用 package.json
檔案中的 main
欄位。
package.json
檔案也必須將 Node.js 適用的 Functions Framework 做為依附元件:
{
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0"
},
"type": "module"
}
主要檔案中的程式碼必須定義函式進入點,並可匯入其他程式碼和 Node.js 模組。主要檔案也可以定義多個函式進入點,並分別部署。
Python
Python 函式的基本目錄結構如下:
. ├── main.py └── requirements.txt
Cloud Run 會從函式目錄根層級名為 main.py
的檔案載入原始碼。主要檔案必須命名為 main.py
。
requirements.txt
檔案必須包含 Python 適用的 Functions Framework 做為依附元件:
functions-framework==3.*
main.py
檔案中的程式碼必須定義函式進入點,並可照常匯入其他程式碼和外部依附元件。main.py
檔案也可以定義多個函式進入點,這些進入點可分別部署。
Go
Go 函式的基本目錄結構如下:
. ├── myfunction.go └── go.mod
函式必須位於專案根目錄的 Go 封裝中。套件及其來源檔案可以有任何名稱,但函式不得位於 package main
中。如果您需要 main
套件 (例如用於本機測試),可以在子目錄中建立:
. ├── myfunction.go ├── go.mod └── cmd/ └── main.go
您的 go.mod
檔案必須包含 Go 適用的 Functions Framework 做為依附元件:
module example.com/my-module
require (
github.com/GoogleCloudPlatform/functions-framework-go v1.5.2
)
根套件中的程式碼必須定義函式進入點,並可照常從子套件和依附元件匯入其他程式碼。您的套件也可以定義多個函式進入點,以便分別部署。
詳情請參閱「Go 執行階段總覽」和「Go 適用的 Functions Framework」。
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 Framework 做為依附元件:
...
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.0.4</version>
</dependency>
...
原始碼檔案中的程式碼必須定義函式進入點,並可照常匯入其他程式碼和外部依附元件。來源檔案也可以定義多個函式進入點,以便分別部署。
.NET
.NET 函式的基本目錄結構如下:
. ├── MyFunction.cs └── MyProject.csproj
您可以像處理其他 .NET 原始碼一樣建構專案。來源檔案可以有任何名稱。
專案檔案必須將 .NET 適用的 Functions Framework 納入為依附元件:
...
<PackageReference Include="Google.Cloud.Functions.Hosting" Version="1.0.0" />
...
原始碼檔案中的程式碼必須定義函式進入點,並可照常匯入其他程式碼和外部依附元件。來源檔案也可以定義多個函式進入點,以便分別部署。
Ruby
Ruby 函式的基本目錄結構如下:
. ├── app.rb ├── Gemfile └── Gemfile.lock
Cloud Run 會從函式目錄根層級名為 app.rb
的檔案載入原始碼。主要檔案必須命名為 app.rb
。
您的 Gemfile
檔案必須包含 Ruby 適用的函式架構做為依附元件:
source "https://rubygems.org"
gem "functions_framework", "~> 1.0"
app.rb
檔案中的程式碼必須定義函式進入點,並可照常匯入其他程式碼和外部依附元件。app.rb
檔案也可以定義多個函式進入點,這些進入點可分別部署。
PHP
PHP 函式的基本目錄結構如下:
. ├── index.php └── composer.json
Cloud Run 會從函式目錄根層級名為 index.php
的檔案載入原始碼。主要檔案必須命名為 index.php
。
composer.json
檔案必須包含 PHP 適用的 Functions Framework 做為依附元件:
{
"require": {
"google/cloud-functions-framework": "^1.1"
}
}
index.php
檔案中的程式碼必須定義函式進入點,並可照常匯入其他程式碼和外部依附元件。index.php
檔案也可以定義多個函式進入點,這些進入點可個別部署。
如果將多個函式分組到單一專案中,請注意,每個函式最後可能會共用同一組依附元件。不過,部分函式可能不需要所有依附元件。
如有可能,建議您將大型多功能程式碼庫分割開來,並將每個函式放在自己的頂層目錄中 (如上述範例所示),且各自擁有自己的來源和專案設定檔。這種做法可盡量減少特定函式所需的依附元件數量,進而減少函式所需的記憶體容量。
編寫 HTTP 函式
如要透過 HTTP(S) 要求叫用函式,請編寫 HTTP 函式。如要允許 HTTP 語意,請使用 Function Framework 並指定 HTTP 函式簽章,接受 HTTP 專屬引數。
以下範例顯示各個執行階段的基本 HTTP 函式來源檔案。如需完整的工作範例,請參閱「使用 Google Cloud CLI 部署 Cloud Run 函式」。如要進一步瞭解來源程式碼的位置,請參閱「來源目錄結構」。
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 Framework 註冊 HTTP 處理常式函式。HTTP 處理常式函式必須是 Express 中介軟體函式,可接受 request 和 response 引數,並傳送 HTTP 回應。
Cloud Run 會根據要求的 Content-Type
標頭,使用 body-parser
自動剖析要求主體,因此您可以在 HTTP 處理常式中存取 req.body
和 req.rawBody
物件。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 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 Framework 註冊 HTTP 處理常式函式。HTTP 處理常式函式必須接受 Flask 要求物件做為引數,並傳回 Flask 可轉換為 HTTP 回應物件的值。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 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 Framework 註冊 HTTP 處理常式函式。HTTP 處理常式函式必須使用標準 http.HandlerFunc
介面傳送 HTTP 回應。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 MyHTTPFunction
。
HTTP 處理常式函式必須實作標準 http.HandlerFunc
介面。這個函式會接受 http.ResponseWriter 介面,函式會使用這個介面建立要求的回覆,以及指向 http.Request 結構體的指標,其中包含傳入 HTTP 要求的詳細資料。
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
方法會收到 HttpRequest
物件,其中說明傳入的 HTTP 要求,以及 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 Framework,透過 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 Framework 註冊 HTTP 處理常式函式。您的 HTTP 處理常式函式必須接受 Rack 要求物件做為引數,並傳回可用於 HTTP 回應的值。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 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 Framework 註冊 HTTP 處理常式函式。HTTP 處理常式函式必須接受實作 PSR-7 ServerRequestInterface
介面的引數,並以字串或實作 PSR-7 ResponseInterface
介面的物件形式,傳回 HTTP 回應。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 myHttpFunction
。
HTTP 要求和回應
使用 Functions Framework 註冊 HTTP 處理常式函式時,HTTP 處理常式可以檢查要求方法,並根據方法執行不同動作。
將事件供應商設為傳送 HTTP 要求至 Cloud Run 函式時,函式會傳送 HTTP 回應。如果函式建立背景工作 (例如使用執行緒、Future、JavaScript Promise 物件、回呼或系統程序),您必須先終止或解決這些工作,才能傳送 HTTP 回應。在傳送 HTTP 回應前未終止的任何工作可能無法完成,且可能導致未定義的行為。
處理 CORS
跨源資源共享 (CORS) 可讓在一個網域執行的應用程式存取另一個網域的資源。舉例來說,您可能需要允許網域向 Cloud Run 函式網域發出要求,才能存取函式。
如要允許對函式提出跨來源要求,請在 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 限制
對於預檢的跨來源要求,預檢 OPTIONS 要求會在沒有 Authorization 標頭的情況下傳送,因此會在所有需要驗證的 HTTP 函式中遭到拒絕。由於預檢要求失敗,主要要求也會失敗。如要解決這項限制,請使用下列其中一個選項:
- 允許公開存取函式。
- 為函式設定 Identity-Aware Proxy,為傳送至 Cloud Run 函式的預檢 OPTIONS 要求提供 Authorization 標頭。
- 將網頁應用程式和 Cloud Run 託管在相同網域,避免發生 CORS 問題。 如要這麼做,請整合 Firebase 託管與 Cloud Run 函式。
編寫事件導向函式
如要讓函式直接回應 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 Framework 註冊 CloudEvent 處理常式函式。處理常式函式必須接受 CloudEvent
物件做為引數。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 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 Framework 註冊 CloudEvent 處理常式函式。處理常式函式必須接受 CloudEvent
物件做為引數。
函式進入點是向 Functions Framework 註冊的處理常式函式名稱。在本範例中,進入點為 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 Framework 註冊 CloudEvent 處理常式函式。處理常式函式必須接受 CloudEvents event.Event
物件做為引數。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 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 Framework,透過 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 Framework 註冊 CloudEvent 處理常式函式。處理常式函式必須接受 CloudEvents
Event
物件做為引數。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 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 Framework 註冊 CloudEvent 處理常式函式。處理常式函式必須接受符合 CloudEventInterface
介面的引數。
函式進入點是處理常式向 Functions Framework 註冊的名稱。在本範例中,進入點為 myCloudEventFunction
。
如果是事件驅動函式,事件資料會以 CloudEvents 格式傳遞至函式,並包含與觸發函式的事件類型相應的 CloudEvent 資料酬載。如要瞭解支援的觸發條件、事件類型和相關聯的事件資料格式,請參閱「函式觸發條件」。
Google Events 存放區包含的資源,可用於處理 Google 發布的 CloudEvents。
函式終止
Cloud Run 會在函式傳回時,將事件驅動函式執行視為完成。如果函式會建立背景工作 (例如使用執行緒、Future、JavaScript Promise 物件、回呼或系統程序),您必須在函式傳回前終止或解決這些工作。函式傳回前未終止的任何工作可能無法完成,且可能導致未定義的行為。
自動重試
您可以設定事件驅動函式,在呼叫失敗時自動重試。詳情請參閱「重試事件驅動函式」。