定義 Go 格式的 Lambda 函數處理常式 - AWS Lambda

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

定義 Go 格式的 Lambda 函數處理常式

Lambda 函數處理常式是您的函數程式碼中處理事件的方法。當有人呼叫您的函數時,Lambda 會執行處理常式方法。函數會執行,直到處理常式傳回回應、結束或逾時為止。

此頁面說明如何使用 Go 處理 Lambda 函數處理常式,包括專案設定、命名慣例和最佳實務。此頁面還包含一個 Go Lambda 函數的範例,該函數會取得訂單的相關資訊、產生文字檔案收據,並將此檔案放入 Amazon Simple Storage Service (S3) 儲存貯體。如需編寫函數後如何部署函數的詳細資訊,請參閱使用 .zip 封存檔部署 Go Lambda 函數使用容器映像來部署 Go Lambda 函數

設定 Go 處理常式專案

Go 撰寫的 Lambda 函數被製作成 Go 可執行檔。與初始化任何其他 Go 專案相同,您可以使用以下 go mod init 命令初始化一個 Go Lambda 函數專案:

go mod init example-go

此處 example-go 是模組名稱。您可以將其取代為任何值。此命令會初始化專案,並產生列有專案相依項的 go.mod 檔案。

使用 go get 命令將任何外部相依項新增至專案。例如,對於以 Go 編寫的所有 Lambda 函數程式碼,您需要納入 github.com/aws/aws-lambda-go/lambda 套件,它可針對 Go 實作 Lambda 程式設計模型。使用以下 go get 命令納入此套件:

go get github.com/aws/aws-lambda-go

函數程式碼應該存在於 Go 檔案中。在以下範例中,我們將此檔案命名為 main.go。在此檔案中,您會在處理常式方法中實作核心函數邏輯,以及呼叫此處理常式的 main() 函數。

範例 Go Lambda 函數程式碼

以下範例 Go Lambda 函數程式碼會取得訂單的相關資訊、產生文字檔案收據,並將此檔案放入 Amazon S3 儲存貯體。

範例 main.go Lambda 函數
package main import ( "context" "encoding/json" "fmt" "log" "os" "strings" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" ) type Order struct { OrderID string `json:"order_id"` Amount float64 `json:"amount"` Item string `json:"item"` } var ( s3Client *s3.Client ) func init() { // Initialize the S3 client outside of the handler, during the init phase cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalf("unable to load SDK config, %v", err) } s3Client = s3.NewFromConfig(cfg) } func uploadReceiptToS3(ctx context.Context, bucketName, key, receiptContent string) error { _, err := s3Client.PutObject(ctx, &s3.PutObjectInput{ Bucket: &bucketName, Key: &key, Body: strings.NewReader(receiptContent), }) if err != nil { log.Printf("Failed to upload receipt to S3: %v", err) return err } return nil } func handleRequest(ctx context.Context, event json.RawMessage) error { // Parse the input event var order Order if err := json.Unmarshal(event, &order); err != nil { log.Printf("Failed to unmarshal event: %v", err) return err } // Access environment variables bucketName := os.Getenv("RECEIPT_BUCKET") if bucketName == "" { log.Printf("RECEIPT_BUCKET environment variable is not set") return fmt.Errorf("missing required environment variable RECEIPT_BUCKET") } // Create the receipt content and key destination receiptContent := fmt.Sprintf("OrderID: %s\nAmount: $%.2f\nItem: %s", order.OrderID, order.Amount, order.Item) key := "receipts/" + order.OrderID + ".txt" // Upload the receipt to S3 using the helper method if err := uploadReceiptToS3(ctx, bucketName, key, receiptContent); err != nil { return err } log.Printf("Successfully processed order %s and stored receipt in S3 bucket %s", order.OrderID, bucketName) return nil } func main() { lambda.Start(handleRequest) }

main.go 檔案包含以下程式碼區段:

  • package main:在 Go 中,包含 func main() 函數的套件一律要命名為 main

  • import 區塊:用於納入 Lambda 函數所需的程式碼。

  • type Order struct {} 區塊:定義此 Go struct 中預期輸入事件的形狀。

  • var () 區塊:用於定義將在 Lambda 函數中使用的任何全域變數。

  • func init() {}:在此 init() 方法的中包含您希望 Lambda 在初始化階段執行的任何程式碼。

  • func uploadReceiptToS3(...) {}:這是主要 handleRequest 處理常式方法所參考的協助程式方法。

  • func handleRequest(ctx context.Context, event json.RawMessage) error {}:這是主要處理常式方法,其中包含應用程式的主要邏輯。

  • func main() {}:這是 Lambda 處理常式的必要進入點。lambda.Start() 方法的引數是主要處理常式方法。

若要讓此函數正常運作,其執行角色必須允許 s3:PutObject 動作。此外,請確保定義 RECEIPT_BUCKET 環境變數。成功調用後,Amazon S3 儲存貯體應包含收據檔案。

處理常式命名慣例

對於以 Go 編寫的 Lambda 函數,處理常式的命名不受限制。在此範例中,處理常式方法的名稱為 handleRequest。若要參考程式碼中的處理常式值,您可以使用 _HANDLER 環境變數。

對於使用 .zip 部署套件部署的 Go 函數,含有函數程式碼的可執行檔必須命名為 bootstrap。此外,bootstrap 檔案必須位於 .zip 檔案的根層級。對於使用容器映像部署的 Go 函數,可執行檔的名稱不受限制。

定義和存取輸入事件物件

JSON 是 Lambda 函數最常見的標準輸入格式。在此範例中,函數預期輸入類似以下內容:

{ "order_id": "12345", "amount": 199.99, "item": "Wireless Headphones" }

使用以 Go 編寫的 Lambda 函數時,您可以將預期輸入事件的形狀定義為 Go struct。在此範例中,我們透過定義 struct 來代表 Order

type Order struct { OrderID string `json:"order_id"` Amount float64 `json:"amount"` Item string `json:"item"` }

此 struct 符合預期的輸入形狀。定義 struct 之後,可以撰寫處理常式簽章,該簽章採用與 encoding/json standard library 相容的一般 JSON 類型。然後,可以使用 func Unmarshal 函數將其還原序列化至該 struct。這在處理常式的前幾行中得以曾現:

func handleRequest(ctx context.Context, event json.RawMessage) error { // Parse the input event var order Order if err := json.Unmarshal(event, &order); err != nil { log.Printf("Failed to unmarshal event: %v", err) return err ... }

在此還原序列化操作之後,您可以存取 order 變數的欄位。例如,使用 "order_id" 可以從原始輸入擷取 order.OrderID 的值。

注意

encoding/json 套件只能存取匯出的欄位。若要匯出,事件結構中的欄位名稱必須大寫。

存取和使用 Lambda 內容物件

Lambda 內容物件 包含有關調用、函數以及執行環境的資訊。在此範例中,我們在處理常式簽章中將此變數宣告為 ctx

func handleRequest(ctx context.Context, event json.RawMessage) error { ... }

ctx context.Context 輸入是函數處理常式的一個選用引數。如需可接受處理常式簽章的詳細資訊,請參閱Go 處理常式的有效處理常式簽章

如果您使用 AWS SDK 呼叫其他服務,則有幾個關鍵區域需要內容物件。例如,您可以使用內容物件載入正確的 AWS SDK 組態,以正確初始化 SDK 用戶端。如下所示:

// Load AWS SDK configuration using the default credential provider chain cfg, err := config.LoadDefaultConfig(ctx)

SDK 呼叫本身可能需要內容物件做為輸入。例如,s3Client.PutObject 呼叫接受內容物件做為其第一個引數:

// Upload the receipt to S3 _, err = s3Client.PutObject(ctx, &s3.PutObjectInput{ ... })

除了 AWS SDK 請求之外,您也可以將內容物件用於函數監控。如需內容物件的詳細資訊,請參閱使用 Lambda 內容物件擷取 Go 函數資訊

Go 處理常式的有效處理常式簽章

當您以 Go 建置 Lambda 函數處理常式時,會有數個選項,但務必堅守以下規則:

  • 處理常式必須是一個函式。

  • 處理常式採用 0 到 2 之間的引數。如果有兩個引數,第一個引數必須實作 context.Context

  • 處理常式傳回 0 到 2 之間的引數。若有單一傳回值,它必須實作 error。如果有兩個傳回值,第二個值必須實作 error

下方將列出有效的處理常式簽章。TInTOut 皆代表與 encoding/json 標準程式庫相容的類型。如需詳細資訊,請參閱 func Unmarshal 以了解這些類型如何還原序列化。

  • func ()
  • func () error
  • func () (TOut, error)
  • func (TIn) error
  • func (TIn) (TOut, error)
  • func (context.Context) error
  • func (context.Context) (TOut, error)
  • func (context.Context, TIn) error
  • func (context.Context, TIn) (TOut, error)

在處理常式中使用 AWS SDK for Go v2

通常,您會使用 Lambda 函數與其他 AWS 資源互動或進行更新。與這些資源互動的最簡單方法便是使用 AWS SDK for Go v2。

注意

AWS SDK for Go (v1) 目前處於維護模式,並且將於 2025 年 7 月 31 日終止支援。我們建議您在之後僅使用 AWS SDK for Go v2。

若要將 SDK 相依項新增至函數,請針對您需要的特定 SDK 用戶端使用 go get 命令。在前面的範例程式碼中,我們使用了 config 程式庫和 s3 程式庫。請在包含 go.modmain.go 檔案的目錄中執行下列命令,來新增這些相依項:

go get github.com/aws/aws-sdk-go-v2/config go get github.com/aws/aws-sdk-go-v2/service/s3

然後,在函數的 import 區塊中相應地匯入相依項:

import ( ... "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" )

在處理常式中使用 SDK 時,須對用戶端進行正確設定。最簡單的方法是使用預設憑證提供者鏈。此範例說明了載入此組態的其中一種方式:

// Load AWS SDK configuration using the default credential provider chain cfg, err := config.LoadDefaultConfig(ctx) if err != nil { log.Printf("Failed to load AWS SDK config: %v", err) return err }

將此組態載入 cfg 變數後,可以將此變數傳遞至用戶端執行個體。範例程式碼會執行個體化一個 Amazon S3 用戶端,如下所示:

// Create an S3 client s3Client := s3.NewFromConfig(cfg)

在此範例中,我們在 init() 函數中初始化 Amazon S3 用戶端,以避免每次調用函數時都必須初始化它。問題是,在 init() 函數中,Lambda 無法存取內容物件。為解決此問題,您可以在初始化階段傳遞類似 context.TODO() 這樣的預留位置。稍後,當您使用用戶端進行呼叫時,再傳遞完整的內容物件。在 AWS SDK 用戶端初始化和呼叫中使用內容對此解決方法也有所說明。

設定並初始化 SDK 用戶端之後,您就可以使用它與其他 AWS 服務互動。此範例程式碼會呼叫 Amazon S3 PutObject API,如下所示:

_, err = s3Client.PutObject(ctx, &s3.PutObjectInput{ Bucket: &bucketName, Key: &key, Body: strings.NewReader(receiptContent), })

存取環境變數

在處理常式程式碼中,您可以使用 os.Getenv() 方法參考任何環境變數。在此範例中,我們使用以下程式碼來參考定義的 RECEIPT_BUCKET 環境變數:

// Access environment variables bucketName := os.Getenv("RECEIPT_BUCKET") if bucketName == "" { log.Printf("RECEIPT_BUCKET environment variable is not set") return fmt.Errorf("missing required environment variable RECEIPT_BUCKET") }

使用全域狀態

為了避免每次調用函數時建立新的資源,您可以在處理常式程式碼之外宣告和修改 Lambda 函數的全域變數。您可以在 var 區塊或陳述式中定義這些全域變數。此外,處理常式可能會宣告在初始化階段執行的 init() 函數。在 AWS Lambda 中,init 方法的行為與標準的 Go 程式相同。

Go Lambda 函數的程式碼最佳實務

請遵循下列清單中的準則,在建置 Lambda 函數時使用最佳編碼實務:

  • 區隔 Lambda 處理常式與您的核心邏輯。能允許您製作更多可測單位的函式。

  • 最小化依存項目的複雜性。偏好更簡易的框架,其可快速在執行環境啟動時載入。

  • 將部署套件最小化至執行時期所必要的套件大小。這能減少您的部署套件被下載與呼叫前解壓縮的時間。

  • 請利用執行環境重新使用來改看函式的效能。在函式處理常式之外初始化 SDK 用戶端和資料庫連線,並在本機快取 /tmp 目錄中的靜態資產。由您函式的相同執行個體處理的後續叫用可以重複使用這些資源。這可藉由減少函數執行時間來節省成本。

    若要避免叫用間洩漏潛在資料,請不要使用執行環境來儲存使用者資料、事件,或其他牽涉安全性的資訊。如果您的函式依賴無法存放在處理常式內記憶體中的可變狀態,請考慮為每個使用者建立個別函式或個別函式版本。

  • 使用 Keep-Alive 指令維持持續連線的狀態。Lambda 會隨著時間的推移清除閒置連線。叫用函數時嘗試重複使用閒置連線將導致連線錯誤。若要維護持續連線,請使用與執行階段相關聯的 keep-alive (保持啟用) 指令。如需範例,請參閱在 Node.js 中重複使用 Keep-Alive 的連線

  • 使用環境變數將操作參數傳遞給您的函數。例如,如果您正在寫入到 Amazon S3 儲存貯體,而非對您正在寫入的儲存貯體名稱進行硬式編碼,請將儲存貯體名稱設定為環境變數。

  • 避免在 Lambda 函數中使用遞迴調用,其中函數會調用自己或啟動可能再次調用函數的程序。這會導致意外的函式呼叫量與升高的成本。若您看到意外的調用數量,當更新程式碼時,請立刻將函數的預留並行設為 0,以調節對函數的所有調用。

  • 請勿在您的 Lambda 函數程式碼中使用未記錄的非公有 API。對於 AWS Lambda 受管執行時間,Lambda 會定期將安全性和函數更新套用至 Lambda 的內部 API。這些內部 API 更新可能是向後不相容的,這會導致意外結果,例如若您的函數依賴於這些非公有 API,則叫用失敗。請參閱 API 參考查看公開可用 API 的清單。

  • 撰寫等冪程式碼。為函數撰寫等冪程式碼可確保採用相同方式來處理重複事件。程式碼應正確驗證事件並正常處理重複的事件。如需詳細資訊,請參閱 How do I make my Lambda function idempotent? (如何讓 Lambda 函數等冪?)。