本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
在 Rust 中定義 Lambda 事件函數處理常式
Lambda 事件函數處理常式是函數程式碼中處理事件的方法。當有人呼叫您的函數時,Lambda 會執行處理常式方法。函數會執行,直到處理常式傳回回應、結束或逾時為止。
此頁面說明如何在 Rust 中使用 Lambda 事件函數處理常式,包括專案初始化、命名慣例和最佳實務。本頁還提供了一個 Rust Lambda 函式範例,該函式會取得訂單的相關資訊、產生文字檔案收據,並將此檔案放入 Amazon Simple Storage Service (S3) 儲存貯體。如需有關編寫函式後如何部署的詳細資訊,請參閱使用 .zip 檔案封存部署 Rust Lambda 事件函數。
主題
設定 Rust 處理常式專案
在 Rust 中使用 Lambda 事件函數時,程序涉及編寫程式碼、編譯程式碼,以及將編譯的成品部署至 Lambda。在 Rust 中設定 Lambda 處理常式專案,最簡單的方法便是使用 AWS Lambda Runtime for Rustlambda_runtime),支援在 Rust 中寫入 Lambda 函數並連接 AWS Lambda的執行環境。
使用下列命令來安裝 Cargo Lambda
cargo install cargo-lambda
成功安裝 cargo-lambda 之後,使用下列命令來初始化新的 Rust Lambda 函式處理常式專案:
cargo lambda new example-rust
當您執行此命令時,命令列介面 (CLI) 會詢問幾個有關 Lambda 函式的問題:
-
HTTP 函數 – 如果您想要透過 API Gateway 或函數 URL 叫用函數,請回答是。否則,請回答否。在本頁的範例程式碼中,由於我們使用自訂 JSON 事件來調用函式,因此回答否。
-
事件類型:如果您想要使用預先定義的事件結構來調用函式,請選取正確的預期事件類型。否則,請將此選項保留空白。在本頁的範例程式碼中,由於我們使用自訂 JSON 事件來調用函式,因此將此選項保留空白。
命令成功執行後,輸入專案的主目錄:
cd example-rust
此命令會在 src 目錄中產生 generic_handler.rs 檔案和 main.rs 檔案。generic_handler.rs 可用於自訂泛型事件處理常式。main.rs 檔案包含應用程式的主要邏輯。Cargo.toml 檔案包含有關套件的中繼資料,並列出其外部相依項。
Rust Lambda 事件函數程式碼範例
以下 Rust Lambda 函式程式碼範例會取得訂單的相關資訊、產生文字檔案收據,並將此檔案放入 Amazon S3 儲存貯體。
範例 main.rs Lambda 事件函數
use aws_sdk_s3::{Client, primitives::ByteStream}; use lambda_runtime::{run, service_fn, Error, LambdaEvent}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::env; #[derive(Deserialize, Serialize)] struct Order { order_id: String, amount: f64, item: String, } async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> { let payload = event.payload; // Deserialize the incoming event into Order struct let order: Order = serde_json::from_value(payload)?; let bucket_name = env::var("RECEIPT_BUCKET") .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?; let receipt_content = format!( "OrderID: {}\nAmount: ${:.2}\nItem: {}", order.order_id, order.amount, order.item ); let key = format!("receipts/{}.txt", order.order_id); let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let s3_client = Client::new(&config); upload_receipt_to_s3(&s3_client, &bucket_name, &key, &receipt_content).await?; Ok("Success".to_string()) } async fn upload_receipt_to_s3( client: &Client, bucket_name: &str, key: &str, content: &str, ) -> Result<(), Error> { client .put_object() .bucket(bucket_name) .key(key) .body(ByteStream::from(content.as_bytes().to_vec())) // Fixed conversion .content_type("text/plain") .send() .await?; Ok(()) } #[tokio::main] async fn main() -> Result<(), Error> { run(service_fn(function_handler)).await }
此 main.rs 檔案包含以下程式碼區段:
-
use陳述式:用於匯入 Lambda 函式所需的 Rust Crate 與方法。 -
#[derive(Deserialize, Serialize)]:定義此 Rust 結構體中預期輸入事件的結構。 -
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>:這是主要處理常式方法,其中包含應用程式的主要邏輯。 -
async fn upload_receipt_to_s3 (...):此為主要function_handler方法所參照的輔助方法。 -
#[tokio::main]:此為標記 Rust 程式進入點的巨集。其還會設定 Tokio 執行時期,允許 main()方法使用async/await並以非同步方式執行。 -
async fn main() -> Result<(), Error>:main()函式是程式碼的進入點。我們會在其中將function_handler指定為主要處理常式方法。
下列 Cargo.toml 為此函式的配套檔案。
[package] name = "example-rust" version = "0.1.0" edition = "2024" [dependencies] aws-config = "1.5.18" aws-sdk-s3 = "1.78.0" lambda_runtime = "0.13.0" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] }
若要讓此函式正常運作,其執行角色必須允許 s3:PutObject 動作。此外,請確保定義 RECEIPT_BUCKET 環境變數。成功調用後,Amazon S3 儲存貯體應包含收據檔案。
Rust 處理常式的有效類別定義
在大多數情況下,您在 Rust 中定義的 Lambda 處理常式簽章格式如下:
async fn function_handler(event: LambdaEvent<T>) -> Result<U, Error>
對於此處理常式:
-
此處理常式名稱為
function_handler。 -
處理常式的單一輸入參數為 event,類型為
LambdaEvent<T>。-
LambdaEvent是來自lambda_runtime套件的包裝函式。借助此包裝函式,您可以存取內容物件,其中包含 Lambda 特定的中繼資料,例如調用的請求 ID。 -
T是反序列化事件類型。例如,這可以是serde_json::Value,允許處理常式接收所有泛型 JSON 輸入。或者,如果函式需要預先定義的特定輸入類型,這可以是ApiGatewayProxyRequest等類型。
-
-
處理常式的傳回類型為
Result<U, Error>。-
U是反序列化輸出類型。U必須實作serde::Serialize特徵,Lambda 才能將傳回值轉換為 JSON 格式。舉例而言,U可以是簡單類型,例如String、serde_json::Value或自訂結構體 (只要實作了Serialize)。當程式碼到達 Ok(U) 陳述式時,表示執行成功,且函式會傳回類型為U的值。 -
當您的程式碼遇到錯誤 (即
Err(Error)) 時,您的函數會在 Amazon CloudWatch 中記錄錯誤,並傳回 類型的錯誤回應Error。
-
在我們的範例中,處理常式簽章如下所示:
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>
其他有效的處理常式簽章將會具有下列功能:
-
省略
LambdaEvent包裝函式:如果省略LambdaEvent,則會失去函式中 Lambda 內容物件的存取權。以下為此類簽章的範例:async fn handler(event: serde_json::Value) -> Result<String, Error> -
使用單位類型作為輸入:對於 Rust,您可以使用單位類型來表示空白輸入。這通常用於具有定期排程調用的函式。以下為此類簽章的範例:
async fn handler(_: ()) -> Result<Value, Error>
處理常式命名慣例
Rust 中的 Lambda 處理常式沒有嚴格的命名限制。雖然您可以為處理常式使用任何名稱,但 Rust 中的函式名稱通常採用 snake_case。
對於較小的應用程式,例如在此範例中,您可以使用單一的 main.rs 檔案來包含所有程式碼。對於較大的專案,main.rs 應該包含函式的進入點,但您可透過額外的檔案將程式碼拆分為邏輯模組。例如,您可能擁有下列檔案結構:
/example-rust │── src/ │ ├── main.rs # Entry point │ ├── handler.rs # Contains main handler │ ├── services.rs # [Optional] Back-end service calls │ ├── models.rs # [Optional] Data models │── Cargo.toml
定義和存取輸入事件物件
JSON 是 Lambda 事件函數最常見的標準輸入格式。在此範例中,函數預期輸入類似以下內容:
{ "order_id": "12345", "amount": 199.99, "item": "Wireless Headphones" }
在 Rust 中,您可透過結構體定義預期輸入事件的結構。在此範例中,我們透過定義以下結構體來表示 Order:
#[derive(Deserialize, Serialize)] struct Order { order_id: String, amount: f64, item: String, }
此 struct 符合預期的輸入形狀。在此範例中,#[derive(Deserialize, Serialize)] 巨集會自動產生用於序列化和反序列化的程式碼。這意味著我們可透過 serde_json::from_value() 方法,將泛型輸入 JSON 類型反序列化至結構體中。這在處理常式的前幾行中得以曾現:
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> { let payload = event.payload; // Deserialize the incoming event into Order struct let order: Order = serde_json::from_value(payload)?; ... }
然後,您可以存取該物件的欄位。例如,使用 order_id 可以從原始輸入擷取 order.order_id 的值。
預先定義的輸入事件類型
aws_lambda_events 套件中有許多預先定義的可用輸入事件類型。例如,若要使用 API Gateway 調用函式,需包含下列匯入:
use aws_lambda_events::event::apigw::ApiGatewayProxyRequest;
然後,確保主要處理常式使用下列簽章:
async fn handler(event: LambdaEvent<ApiGatewayProxyRequest>) -> Result<String, Error> { let body = event.payload.body.unwrap_or_default(); ... }
如需有關其他預先定義輸入事件類型的詳細資訊,請參閱 aws_lambda_events crate
存取和使用 Lambda 內容物件
Lambda 內容物件 包含有關調用、函數以及執行環境的資訊。在 Rust 中,LambdaEvent 包裝函式包含內容物件。例如,您可以使用內容物件,透過下列程式碼擷取目前調用的請求 ID:
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> { let request_id = event.context.request_id; ... }
如需內容物件的詳細資訊,請參閱使用 Lambda 內容物件擷取 Rust 事件函數資訊。
在處理常式 適用於 Rust 的 AWS SDK 中使用
通常,您將使用 Lambda 函數與其他 AWS 資源互動或進行更新。與這些資源互動的最簡單方法便是使用 適用於 Rust 的 AWS SDK。
若要將 SDK 相依項新增至函式,請在 Cargo.toml 檔案中納入這些相依相。建議僅新增函式所需的程式庫。在先前的範例程式碼中,我們使用了 aws_sdk_s3::Client。在 Cargo.toml 檔案中,您可以透過在 [dependencies] 區段下新增以下行來納入此相依項:
aws-sdk-s3 = "1.78.0"
注意
這可能不是最新版本。請為應用程式選擇適當的版本。
然後,直接在程式碼中匯入相依項:
use aws_sdk_s3::{Client, primitives::ByteStream};
隨後,範例程式碼會初始化 Amazon S3 用戶端,如下所示:
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let s3_client = Client::new(&config);
初始化 SDK 用戶端之後,您可以使用它來與其他 AWS 服務互動。範例程式碼會在 upload_receipt_to_s3 輔助函式中呼叫 Amazon S3 PutObject API。
存取環境變數
在處理常式程式碼中,您可以使用 env::var 方法來引用任何環境變數。在此範例中,我們使用以下程式碼來參考定義的 RECEIPT_BUCKET 環境變數:
let bucket_name = env::var("RECEIPT_BUCKET") .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?;
使用共用狀態
您可以宣告獨立於 Lambda 函數處理常式程式碼的共用變數。這些變數可協助您在函數接收任何事件之前在 初始化階段 過程中載入狀態資訊。例如,您可以透過更新 main 函式和處理常式簽章,將本頁的程式碼修改為在初始化 Amazon S3 用戶端時使用共用狀態:
async fn function_handler(client: &Client, event: LambdaEvent<Value>) -> Result<String, Error> { ... upload_receipt_to_s3(client, &bucket_name, &key, &receipt_content).await?; ... } ... #[tokio::main] async fn main() -> Result<(), Error> { let shared_config = aws_config::from_env().load().await; let client = Client::new(&shared_config); let shared_client = &client; lambda_runtime::run(service_fn(move |event: LambdaEvent<Request>| async move { handler(&shared_client, event).await })) .await
Rust Lambda 事件函數的程式碼最佳實務
請遵循下列清單中的準則,在建置 Lambda 函數時使用最佳編碼實務:
-
區隔 Lambda 處理常式與您的核心邏輯。這可讓您更輕鬆地建立更可供單位測試的函數。
-
最小化依存項目的複雜性。偏好更簡易的框架,其可快速在執行環境啟動時載入。
-
將部署套件最小化至執行時間所必要的套件大小。這可減少在叫用之前下載和解壓縮部署套件所需的時間。
請利用執行環境重新使用來改看函式的效能。在函式處理常式之外初始化 SDK 用戶端和資料庫連線,並在本機快取 /tmp 目錄中的靜態資產。由您函式的相同執行個體處理的後續叫用可以重複使用這些資源。這可藉由減少函數執行時間來節省成本。
若要避免叫用間洩漏潛在資料,請不要使用執行環境來儲存使用者資料、事件,或其他牽涉安全性的資訊。如果您的函式依賴無法存放在處理常式內記憶體中的可變狀態,請考慮為每個使用者建立個別函式或個別函式版本。
使用 Keep-Alive 指令維持持續連線的狀態。Lambda 會隨著時間的推移清除閒置連線。叫用函數時嘗試重複使用閒置連線將導致連線錯誤。若要維護持續連線,請使用與執行階段相關聯的 keep-alive (保持啟用) 指令。如需範例,請參閱在 Node.js 中重複使用 Keep-Alive 的連線。
使用環境變數將操作參數傳遞給您的函數。例如,如果您正在寫入到 Amazon S3 儲存貯體,而非對您正在寫入的儲存貯體名稱進行硬式編碼,請將儲存貯體名稱設定為環境變數。
避免在 Lambda 函數中使用遞迴調用,其中函數會調用自己或啟動可能再次調用函數的程序。這會導致意外的函式呼叫量與升高的成本。若您看到意外的調用數量,當更新程式碼時,請立刻將函數的預留並行設為 0,以調節對函數的所有調用。
請勿在您的 Lambda 函數程式碼中使用未記錄的非公有 API。對於 AWS Lambda 受管執行期,Lambda 會定期將安全性和功能更新套用至 Lambda 的內部 APIs。這些內部 API 更新可能是向後不相容的,這會導致意外結果,例如若您的函數依賴於這些非公有 API,則叫用失敗。請參閱 API 參考查看公開可用 API 的清單。
撰寫等冪程式碼。為函數撰寫等冪程式碼可確保採用相同方式來處理重複事件。程式碼應正確驗證事件並正常處理重複的事件。如需詳細資訊,請參閱 How do I make my Lambda function idempotent?