Go の Lambda 関数ハンドラーの定義 - AWS Lambda

Go の Lambda 関数ハンドラーの定義

Lambda 関数ハンドラーは、イベントを処理する関数コード内のメソッドです。関数が呼び出されると、Lambda はハンドラーメソッドを実行します。関数は、ハンドラーが応答を返すか、終了するか、タイムアウトするまで実行されます。

Go ハンドラーの基本

Go で書き込まれた Lambda 関数は、Go 実行可能ファイルとして作成されます。Lambda 関数コードでは、Go の Lambda プログラミングモデルを実装する github.com/aws/aws-lambda-go/lambda パッケージを含める必要があります。加えて、ハンドラー関数および main() 関数の実装が必要となります。

例 Go Lambda 関数
package main import ( "context" "fmt" "github.com/aws/aws-lambda-go/lambda" ) type MyEvent struct { Name string `json:"name"` } func HandleRequest(ctx context.Context, event *MyEvent) (*string, error) { if event == nil { return nil, fmt.Errorf("received nil event") } message := fmt.Sprintf("Hello %s!", event.Name) return &message, nil } func main() { lambda.Start(HandleRequest) }

次に示すのはこの関数の入力例です。

{ "name": "Jane" }

次の点に注意してください。

  • package main: Go では、func main() を含むパッケージは常に main と名付けられることが必要です。

  • インポート: これを使用して、Lambda 関数に必要なライブラリを含めます。この場合、次のものが含まれます。

  • func HandleRequest(ctx context.Context, event *MyEvent) (*string, error): これは Lambda ハンドラーの署名です。Lambda 関数のエントリポイントであり、関数が呼び出されたときに実行されるロジックが含まれています。また、次に示すパラメータも含まれます。

    • ctx context.Context: Lambda 関数呼び出しへのランタイム情報を提供します。ctx は、Lambda コンテキストオブジェクトを使用して Go 関数の情報を取得する を介して利用できる情報を活用する宣言を行う変数です。

    • event *MyEvent: これは MyEvent をポイントする event という名前のパラメータです。Lambda 関数への入力を表します。

    • *string, error: ハンドラーは 2 つの値を返します。1 つ目は、Lambda 関数の結果を含む文字列へのポインタです。2 つ目はエラータイプで、エラーがない場合は nil となり、何か問題が発生した場合は標準的なエラー情報 を含みます。

    • return &message, nil: 2 つの値を返します。1 つ目は文字列メッセージへのポインタです。これは、入力イベントの Name フィールドを使用して作成された挨拶文です。2 つ目の値は nil であり、関数でエラーが発生しなかったことを示します。

  • func main(): Lambda 関数コードが実行されるエントリポイント。これは必須です。

    func main(){} コードの括弧内に lambda.Start(HandleRequest) を追加すると、Lambda 関数が実行されます。Go 言語の規約に基づき、開き中括弧 { は main 関数シグネチャの直後に置く必要があります。

命名

provided.al2 および provided.al2023 ランタイム

.zip デプロイパッケージで provided.al2 または provided.al2023 ランタイムを使用する Go 関数の場合、関数コードを含む実行ファイルの名前は bootstrap である必要があります。.zip ファイルを使用して関数をデプロイする場合、bootstrap ファイルは .zip ファイルのルートにある必要があります。コンテナイメージprovided.al2 または provided.al2023 ランタイムを使用する Go 関数の場合、実行ファイルには任意の名前を使用できます。

ハンドラーには任意の名前を使用できます。コード内でハンドラー値を参照するには、_HANDLER 環境変数を使用できます。

go1.x ランタイム

go1.x ランタイムを使用する Go 関数の場合、実行ファイルとハンドラーは任意の名前を共有できます。例えば、ハンドラーの値を Handler に設定すると、Lambda はHandler 実行可能ファイル内の main() 関数を呼び出します。

Lambda コンソールの関数ハンドラー名を変更するには、ランタイム設定ペインで、[Edit] (編集) を選択します。

構造化されたタイプを使用した Lambda 関数ハンドラー

上記の例では、入力タイプは単純な文字列でした。ただし、構造化されたイベントを関数ハンドラーに渡すこともできます。

package main import ( "fmt" "github.com/aws/aws-lambda-go/lambda" ) type MyEvent struct { Name string `json:"What is your name?"` Age int `json:"How old are you?"` } type MyResponse struct { Message string `json:"Answer"` } func HandleLambdaEvent(event *MyEvent) (*MyResponse, error) { if event == nil { return nil, fmt.Errorf("received nil event") } return &MyResponse{Message: fmt.Sprintf("%s is %d years old!", event.Name, event.Age)}, nil } func main() { lambda.Start(HandleLambdaEvent) }

次に示すのはこの関数の入力例です。

{ "What is your name?": "Jim", "How old are you?": 33 }

レスポンスは次のようになります。

{ "Answer": "Jim is 33 years old!" }

エクスポートするには、イベント構造体のフィールド名が大文字である必要があります。AWS イベントソースでイベントを処理するための詳細は、「aws-lambda-go/events」を参照してください。

有効なハンドラー署名

Go で Lambda 関数ハンドラーを構築するには複数の方法がありますが、次のルールに従う必要があります。

  • ハンドラーは関数である必要があります。

  • ハンドラーは 0 から 2 までの引数を取る場合があります。2 つの引数がある場合は、最初の引数が context.Context を実装する必要があります。

  • ハンドラーは 0 から 2 までの引数を返す場合があります。単一の戻り値がある場合は、この値が error を実装する必要があります。2 つの戻り値がある場合には、2 番目の値が error を実装している必要があります。

次のリストは、有効なハンドラー署名の一覧です。 TInTOut は、 encoding/json 標準ライブラリと互換性のあるタイプを表しています。詳細については、「func アンマーシャリング」でこれらのタイプが逆シリアル化する方法を参照してください。

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

グローバルな状態を使用する

Lambda 関数のハンドラーコードとは別のグローバルな変数を宣言して変更することができます。さらに、ハンドラーがロードされている場合に実行される init 関数をハンドラーが宣言することがあります。これは、AWS Lambda でも標準の Go プログラムと同じように作動します。Lambda 関数の単一のインスタンスが、同時に複数のイベントを処理することはありません。

例 グローバル変数を使用する Go 関数
注記

このコードは AWS SDK for Go V2 を使用します。詳細については、「Getting Started with the AWS SDK for Go V2」を参照してください。

package main import ( "context" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" "log" ) var invokeCount int var myObjects []types.Object func init() { // Load the SDK configuration cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalf("Unable to load SDK config: %v", err) } // Initialize an S3 client svc := s3.NewFromConfig(cfg) // Define the bucket name as a variable so we can take its address bucketName := "amzn-s3-demo-bucket" input := &s3.ListObjectsV2Input{ Bucket: &bucketName, } // List objects in the bucket result, err := svc.ListObjectsV2(context.TODO(), input) if err != nil { log.Fatalf("Failed to list objects: %v", err) } myObjects = result.Contents } func LambdaHandler(ctx context.Context) (int, error) { invokeCount++ for i, obj := range myObjects { log.Printf("object[%d] size: %d key: %s", i, obj.Size, *obj.Key) } return invokeCount, nil } func main() { lambda.Start(LambdaHandler) }

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 リファレンス」を参照してください。

  • 冪等性コードを記述します。関数の記述に冪等性コードを使用すると、重複するイベントが同じ方法で処理されるようになります。コードでは、イベントを適切に検証し、重複するイベントを適切に処理する必要があります。詳細については、「Lambda 関数を冪等にするにはどうすればよいですか?」を参照してください。