在 Java 中 Lambda 義函數處理程序 - AWS Lambda

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

在 Java 中 Lambda 義函數處理程序

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

本指南的 GitHub 存放庫提供了示 easy-to-deploy 範各種處理常式類型的範例應用程式。如需詳細資訊,請參閱本主題的結尾內容

處理常式範例:Java 17 執行期

在下列 Java 17 範例中,名為 HandlerIntegerJava17 的類別會定義名為 handleRequest 的處理常式方法。處理常式方法接受以下輸入:

  • IntegerRecord,這是表示事件資料的自訂 Java 記錄。在此範例中,如下所示定義 IntegerRecord

    record IntegerRecord(int x, int y, String message) { }
  • 內容物件,其提供的方法和各項屬性提供了有關調用、函數以及執行環境的資訊。

假設我們想要撰寫這樣一個函數,其記錄來自輸入 IntegerRecordmessage,並傳回 xy 的總和。以下是函數程式碼:

範例 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. 爪哇
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 }

不過,每個欄位的值必須是字串或數字。如果事件包含具有物件作為值的欄位,執行階段無法將其還原序列化並傳回錯誤。

選擇與函數處理的事件資料搭配使用的輸入類型。您可以使用基本類型或已妥善定義的類型。

輸入類型
  • IntegerLongDouble 等等 - 事件是沒有其他格式的數字,例如 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庫中的類型。

如果您定義了自己的輸入類型,它應該是一個可反序列化的,可變的普通舊 Java 對象(POJO),並為事件中的每個字段提供默認的構造函數和屬性。事件中未對應至屬性的金鑰以及未包含在事件中的屬性都會遭捨棄且不會發生錯誤。

輸出類型可以是物件或 void。執行階段會將傳回的值序列化為文字。如果輸出是具有欄位的物件,則執行階段會將其序列化為JSON文件。如果它是一個包裝基本值的類型,執行階段會傳回代表該值的文字。

處理程式界面

程式aws-lambda-java-core庫定義處理常式方法的兩個介面。使用提供的介面來簡化處理常式組態,並在編譯階段驗證處理常式方法簽章。

RequestHandler 介面是一個一般類型,它有兩個參數:輸入類型和輸出類型。兩種類型都必須是物件。當您使用此介面時,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 值。

下面的 Java 21 示例顯示了如何使用 Lambda 函數來處理訂單。此範例使用緩衝式讀取器和寫入器類型來處理輸入和輸出串流,並示範如何定義要在函數中使用的自訂 Java 記錄。

範例 HandlerStream. 爪哇
import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; public class HandlerStream implements RequestStreamHandler { private static final ObjectMapper objectMapper = new ObjectMapper(); @Override public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { Order order = objectMapper.readValue(input, Order.class); processOrder(order); OrderAccepted orderAccepted = new OrderAccepted(order.orderId); objectMapper.writeValue(output, orderAccepted); } private void processOrder(Order order) { // business logic } public record Order(@JsonProperty("orderId") String orderId, @JsonProperty("items") List<Item> items) { } public record Item(@JsonProperty("name") String name, @JsonProperty("quantity") Integer quantity) { } public record OrderAccepted(@JsonProperty("orderId") String orderId) { } }

Java Lambda 函數的程式碼最佳作法

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

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

  • 控制函數部署套件內的相依性。 AWS Lambda 執行環境包含許多程式庫。若要啟用最新的一組功能與安全更新,Lambda 會定期更新這些程式庫。這些更新可能會為您的 Lambda 函數行為帶來細微的變更。若要完全掌控您函式所使用的相依性,請利用部署套件封裝您的所有相依性。

  • 最小化依存項目的複雜性。偏好更簡易的框架,其可快速在執行環境啟動時載入。例如,偏好更簡易的 Java 相依性置入 (IoC) 架構如 DaggerGuice,勝於複雜的架構如 Spring Framework

  • 將部署套件最小化至執行時間所必要的套件大小。這能減少您的部署套件被下載與呼叫前解壓縮的時間。對於以 Java 編寫的函數,請避免將整個程式 AWS SDK庫做為部署套件的一部分上傳。取而代之的是,SDK您可以選擇取得所需元件的模組 (例如 DynamoDB、Amazon S3 SDK 模組和 Lambda 核心程式庫)。

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

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

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

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

  • 避免在 Lambda 函數中使用遞迴叫用,函數會自行叫用或啟動可能再次叫用函數的程序。這會導致意外的函式呼叫量與升高的成本。如果您看到非預期的叫用量,請在更新程式碼時0立即將保留並行的函數設定為限制函數的所有叫用。

  • 請勿在 Lambda 函數程式碼中使用非公開APIs的非公開文件。對於 AWS Lambda 受管執行階段,Lambda 會定期將安全性和功能更新套用至 Lambda 的內部APIs。這些內部API更新可能向後不兼容,導致意外後果,例如,如果您的函數依賴於這些非公共功能,則會導致調用失敗。APIs如需公開可用的清單,請API參閱參考資料APIs。

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

  • 請避免使用 Java DNS 快取記憶體。Lambda 函數已經快取DNS回應。如果您使用其他DNS快取,則可能會遇到連線逾時的情況。

    java.util.logging.Logger類別可以間接啟用JVMDNS快取。若要覆寫預設設定,請在初始化之前將網路位址 .cache.ttl 設定為 0。 logger範例:

    public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); }

    若要避免UnknownHostException失敗,建議您networkaddress.cache.negative.ttl將設定為 0。您可以使用AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0環境變數為 Lambda 函數設定此屬性。

    停用JVMDNS快取並不會停用 Lambda 的受管理DNS快取。

  • 將相依性 .jar 檔案置於不同的 /lib 目錄,縮短 Lambda 解壓縮以 Java 撰寫之部署套件的時間。這比將您所有函式程式碼全部放入具大量 .class 檔案的單一 jar 更快速。如需說明,請參閱 使用 .zip 或JAR檔案封存來部署 Java Lambda 函數

範例處理常式程式碼

本指南的 GitHub 儲存庫包含範例應用程式,示範各種處理常式類型和介面的使用方式。每個範例應用程式都包含可輕鬆部署和清理的指令碼、 AWS SAM 範本和支援資源。

以 Java 編寫的範例 Lambda 應用程式
  • java17-examples – 一個 Java 函數,示範如何使用 Java 記錄來表示輸入事件資料物件。

  • java-basic - 具有單元測試和變數日誌組態的最小 Java 函數集合。

  • java 事件 — Java 函數的集合,其中包含如何處理來自各種服務(例如 Amazon API 網關,Amazon 和亞馬 Amazon Kinesis)的事件的骨架代碼。SQS這些函數使用最新版本的aws-lambda-java-events庫(3.0.0 及更新版本)。這些範例不需要做 AWS SDK為相依性。

  • s3-java — 一種 Java 函數,可處理來自 Amazon S3 的通知事件,並使用 Java 類別程式庫 (JCL) 從上傳的影像檔建立縮圖。

  • 使用API閘道來叫用 Lambda 函數 — 一種 Java 函數,可掃描包含員工資訊的 Amazon DynamoDB 表格。然後,其會使用 Amazon Simple Notification Service 向員工傳送文字訊息,慶祝他們的工作週年紀念日。此範例使用API閘道來呼叫函數。

java-eventss3-java應用程式會將 AWS 服務事件做為輸入,並傳回字串。java-basic 應用程式包含數種類型的處理常式:

若要測試不同的處理常式類型,只要變更 AWS SAM 範本中的處理常式值即可。如需詳細說明,請參閱範例應用程式的讀我檔案。