Java の AWS Lambda 関数ハンドラー - AWS Lambda

Java の AWS Lambda 関数ハンドラー

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

このガイドの GitHub リポジトリには、さまざまなハンドラータイプを示す easy-to-deploy サンプルアプリケーションが用意されています。詳細については、このトピックの最後を参照してください。

ハンドラーの例: Java 17 ランタイム

以下の Java 17 の例では、HandlerIntegerJava17 という名前のクラスで handleRequest という名前のハンドラーメソッドを定義しています。ハンドラーメソッドは以下の入力を受け取ります。

  • IntegerRecord 。これは、イベントデータを表すカスタム Java レコードです。この例では、IntegerRecord を次のように定義します。

    record IntegerRecord(int x, int y, String message) { }
  • コンテキストオブジェクト。このオブジェクトは、呼び出し、関数、および実行環境に関する情報を示すメソッドおよびプロパティを提供します。

入力 IntegerRecord から message をログに記録し、x と y の合計を返す関数を記述したいとします。関数コードは次のとおりです。

HandlerIntegerJava17.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; // Handler value: example.HandlerInteger public class HandlerIntegerJava17 implements RequestHandler<IntegerRecord, Integer>{ @Override /* * Takes in an InputRecord, which contains two integers and a String. * Logs the String, then returns the sum of the two Integers. */ public Integer handleRequest(IntegerRecord event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("String found: " + event.message()); return event.x() + event.y(); } } record IntegerRecord(int x, int y, String message) { }

関数の設定でハンドラーパラメータを指定することにより、Lambda が呼び出すメソッドを指示します。ハンドラーは次の形式で表現できます。

  • package.Class::method - 完全形式。例えば、example.Handler::handleRequest などです。

  • package.Classハンドラーインターフェイスを実装するクラスの省略形式。例: example.Handler

Lambda がハンドラーを呼び出すと、Lambda ランタイムはイベントを JSON 形式の文字列として受け取り、オブジェクトに変換します。前の例では、サンプルイベントは次のようになります。

event.json
{ "x": 1, "y": 20, "message": "Hello World!" }

このファイルを保存し、次の AWS Command Line Interface (CLI) コマンドを使用して関数をローカルでテストできます。

aws lambda invoke --function-name function_name --payload file://event.json out.json

ハンドラーの例: Java 11 以下のランタイム

Lambda は Java 17 以降のランタイムのレコードをサポートします。すべての Java ランタイムで、クラスを使用してイベントデータを表すことができます。次の例では、整数のリストとコンテキストオブジェクトを入力として受け取り、リスト内のすべての整数の合計を返します。

以下の例では、Handler という名前のクラスで handleRequest という名前のハンドラーメソッドを定義しています。ハンドラーメソッドは、イベントとコンテキストオブジェクトを入力として受け取り、文字列を返します。

HandlerList.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.util.List; // Handler value: example.HandlerList public class HandlerList implements RequestHandler<List<Integer>, Integer>{ @Override /* * Takes a list of Integers and returns its sum. */ public Integer handleRequest(List<Integer> event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("EVENT TYPE: " + event.getClass().toString()); return event.stream().mapToInt(Integer::intValue).sum(); } }

その他の例については、「サンプルハンドラーコード」を参照してください。

初期化コード

Lambda は、関数を初めて呼び出す前の初期化フェーズで静的コードとクラスコンストラクターを実行します。初期化中に作成されたリソースは、呼び出しと呼び出しの間でメモリ内に保持され、ハンドラーによって何千回も再利用できます。したがって、メインハンドラーメソッドの外側に初期化コードを追加することで、計算時間を節約し、複数の呼び出し間でリソースを再利用することができます。

次の例では、クライアント初期化コードはメインハンドラーメソッドの外にあります。ランタイムは、関数が最初のイベントを処理する前にクライアントを初期化します。Lambda がクライアントを再度初期化する必要がないため、後続のイベントはずっと速くなります。

Handler.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.util.Map; import software.amazon.awssdk.services.lambda.LambdaClient; import software.amazon.awssdk.services.lambda.model.GetAccountSettingsResponse; import software.amazon.awssdk.services.lambda.model.LambdaException; // Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String> { private static final LambdaClient lambdaClient = LambdaClient.builder().build(); @Override public String handleRequest(Map<String,String> event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("Handler invoked"); GetAccountSettingsResponse response = null; try { response = lambdaClient.getAccountSettings(); } catch(LambdaException e) { logger.log(e.getMessage()); } return response != null ? "Total code size for your account is " + response.accountLimit().totalCodeSize() + " bytes" : "Error"; } }

入力タイプと出力タイプの選択

イベントがマッピングするオブジェクトのタイプは、ハンドラーメソッドの署名で指定します。上記の例では、Java ランタイムはイベントを、Map<String,String> インターフェイスを実装するタイプに逆シリアル化します。S tring-to-string マップは、次のようなフラットイベントで機能します。

Event.json - 気象データ
{ "temperatureK": 281, "windKmh": -3, "humidityPct": 0.55, "pressureHPa": 1020 }

ただし、各フィールドの値は文字列または数値であることが必要です。イベント内のフィールドにオブジェクトが値として含まれている場合、ランタイムはそれを逆シリアル化できず、エラーを返します。

関数が処理するイベントデータに使用する入力タイプを選択します。基本タイプ、汎用タイプ、または良定義タイプを使用できます。

入力タイプ
  • Integer LongDouble、など - イベントは、追加の形式のない数値です (3.5 など)。ランタイムは、値を指定されたタイプのオブジェクトに変換します。

  • String - イベントは、引用符を含む JSON 文字列です ("My string." など)。ランタイムは、引用符なしの値を String オブジェクトに変換します。

  • TypeMap<String,Type> など - イベントは JSON オブジェクトです。ランタイムは、それを指定されたタイプまたはインターフェイスのオブジェクトに逆シリアル化します。

  • List<Integer> List<String>List<Object>、など - イベントは JSON 配列です。ランタイムは、それを指定されたタイプまたはインターフェイスのオブジェクトに逆シリアル化します。

  • InputStream - イベントは任意の JSON タイプです。ランタイムは、ドキュメントのバイトストリームを変更せずにハンドラーに渡します。入力を逆シリアル化し、出力を出力ストリームに書き込みます。

  • ライブラリタイプ - AWSサービスによって送信されるイベントの場合は、aws-lambda-java-eventsライブラリ内のタイプを使用します。

独自の入力タイプを定義する場合、そのタイプは、逆シリアライズかつ変更可能な Plain Old Java Object (POJO) であり、デフォルトのコンストラクタと、イベントの各フィールドのプロパティが必要です。プロパティにマッピングされないイベントのキー、およびイベントに含まれていないプロパティは、エラーなしでドロップされます。

出力タイプはオブジェクトまたは void です。ランタイムは、戻り値をテキストにシリアル化します。出力が、フィールドを含むオブジェクトの場合、ランタイムは、それを JSON ドキュメントにシリアル化します。出力が、プリミティブ値をラップするタイプの場合、ランタイムは、その値のテキスト表現を返します。

ハンドラーのインターフェイス

aws-lambda-java-core ライブラリは、ハンドラーメソッドの 2 つのインターフェイスを定義します。用意されているインターフェイスを使用して、ハンドラー設定をシンプルにし、コンパイル時にハンドラーメソッドの署名を検証します。

RequestHandler インターフェイスは、入力タイプと出力タイプの 2 つのパラメータを受け取る汎用タイプです。どちらのタイプもオブジェクトであることが必要です。このインターフェイスを使用すると、Java ランタイムはイベントを入力タイプのオブジェクトに逆シリアル化し、出力をテキストにシリアル化します。組み込みのシリアル化が入力タイプと出力タイプで機能する場合は、このインターフェイスを使用します。

Handler.java - ハンドラーインターフェイス
// Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String>{ @Override public String handleRequest(Map<String,String> event, Context context)

独自のシリアル化を使用するには、RequestStreamHandler インターフェイスを実装します。このインターフェイスでは、Lambda はハンドラーに入力ストリームと出力ストリームを渡します。ハンドラーは、入力ストリームからバイトを読み取り、出力ストリームに書き込み、void を返します。

以下の例では、バッファされたリーダーとライターのタイプを使用して、入力ストリームと出力ストリームを処理しています。

HandlerStream.java
import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.LambdaLogger import com.amazonaws.services.lambda.runtime.RequestStreamHandler ... // Handler value: example.HandlerStream public class HandlerStream implements RequestStreamHandler { @Override /* * Takes an InputStream and an OutputStream. Reads from the InputStream, * and copies all characters to the OutputStream. */ public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { LambdaLogger logger = context.getLogger(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("US-ASCII"))); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, Charset.forName("US-ASCII")))); int nextChar; try { while ((nextChar = reader.read()) != -1) { outputStream.write(nextChar); } } catch (IOException e) { e.printStackTrace(); } finally { reader.close(); String finalString = writer.toString(); logger.log("Final string result: " + finalString); writer.close(); } } }

サンプルハンドラーコード

このガイドの GitHub リポジトリには、さまざまなハンドラータイプとインターフェイスの使用方法を示すサンプルアプリケーションが含まれています。各サンプルアプリケーションには、簡易のデプロイとクリーンアップ用のスクリプト、AWS SAM テンプレート、サポートリソースが含まれています。

Java のサンプル Lambda アプリケーション
  • [java17-examples] — Java レコードを使用して入力イベントデータオブジェクトを表現する方法を示す Java 関数。

  • java-basic - 単位テストと変数ログ記録設定を使用する、最小限の Java 関数のコレクション。

  • java-events - Amazon API Gateway、Amazon SQS、Amazon Kinesis などのさまざまなサービスからのイベントを処理する方法のスケルトンコードを含む Java 関数のコレクション。これらの関数は、最新バージョンのaws-lambda-java-eventsライブラリ (3.0.0 以降) を使用します。これらの例では、依存関係としての AWS SDK が不要です。

  • s3-java - Amazon S3 からの通知イベントを処理し、Java Class Library (JCL) を使用して、アップロードされたイメージファイルからサムネイルを作成する Java 関数。

  • API Gateway を使用して Lambda 関数を呼び出す - 従業員情報を含む Amazon DynamoDB テーブルをスキャンする Java 関数。次に、Amazon Simple Notification Service を使用して、仕事の記念日を祝うテキストメッセージを従業員に送信します。この例では、API ゲートウェイを使用して関数を呼び出します。

java-events および s3-java アプリケーションは、AWS サービスのイベントを入力として受け取り、文字列を返します。java-basic アプリケーションには数タイプのハンドラーが含まれています。

さまざまなハンドラータイプをテストするには、AWS SAM テンプレートのハンドラー値を変更するだけです。詳細な手順については、サンプルアプリケーションの readme ファイルを参照してください。