

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

# 最大化 Lambda SnapStart 效能
<a name="snapstart-best-practices"></a>

**Topics**
+ [效能調校](#snapstart-tuning)
+ [網路最佳實務](#snapstart-networking)

## 效能調校
<a name="snapstart-tuning"></a>

若要充分利用 SnapStart 的優勢，請針對您的執行時期考慮下列程式碼最佳化建議。

**注意**  
SnapStart 搭配大規模函數調用使用時效果最佳。不常調用的函數效能改進效果可能不會相同。

### Java
<a name="snapstart-tuning-java"></a>

為了充分利用 SnapStart 的優勢，建議您在初始化程式碼 (而不是函數處理常式) 中預先載入會導致啟動延遲的相依項和初始化資源。這會將與大量類別載入相關聯的延遲移出調用路徑，進而透過 SnapStart 最佳化啟動效能。

如果您無法在初始化期間預先載入相依項和初始化資源，建議您使用虛擬調用預先載入它們。若要執行此作業，請從 AWS Labs GitHub 儲存庫上的[寵物存放區函數](https://github.com/awslabs/aws-serverless-java-container/tree/main/samples/spring/pet-store)更新函數處理常式程式碼，如下列範例所示。

```
private static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
  static {
      try {
          handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class);

          // Use the onStartup method of the handler to register the custom filter
          handler.onStartup(servletContext -> {
              FilterRegistration.Dynamic registration = servletContext.addFilter("CognitoIdentityFilter", CognitoIdentityFilter.class);
              registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
          });

          // Send a fake Amazon API Gateway request to the handler to load classes ahead of time
          ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity();
          identity.setApiKey("foo");
          identity.setAccountId("foo");
          identity.setAccessKey("foo");

          AwsProxyRequestContext reqCtx = new AwsProxyRequestContext();
          reqCtx.setPath("/pets");
          reqCtx.setStage("default");
          reqCtx.setAuthorizer(null);
          reqCtx.setIdentity(identity);

          AwsProxyRequest req = new AwsProxyRequest();
          req.setHttpMethod("GET");
          req.setPath("/pets");
          req.setBody("");
          req.setRequestContext(reqCtx);

          Context ctx = new TestContext();
          handler.proxy(req, ctx);


      } catch (ContainerInitializationException e) {
          // if we fail here. We re-throw the exception to force another cold start
          e.printStackTrace();
          throw new RuntimeException("Could not initialize Spring framework", e);
      }
  }
```

### Python
<a name="snapstart-tuning-python"></a>

若要充分利用 SnapStart 的優勢，在編寫 Python 函數時應重視高效率程式碼組織和資源管理。一般而言，應在[初始化階段](lambda-runtime-environment.md#runtimes-lifecycle-ib)執行繁重的運算任務。這種方法會將耗時的作業移出調用路徑，進而改善函數的整體效能。為協助您有效實作此策略，我們提供以下最佳實務建議：
+ 在函數處理常式外部匯入相依項。
+ 在處理常式外部建立 `boto3` 執行個體。
+ 在調用處理常式之前初始化靜態資源或組態。
+ 請考慮使用快照前[執行時期勾點](snapstart-runtime-hooks-python.md)處理資源密集型任務，例如下載外部檔案、預先載入 Django 等架構，或載入機器學習模型。

**Example ：針對 SnapStart 最佳化 Python 函數**  

```
# Import all dependencies outside of Lambda handler
from snapshot_restore_py import register_before_snapshot
import boto3
import pandas
import pydantic

# Create S3 and SSM clients outside of Lambda handler
s3_client = boto3.client("s3")

# Register the function to be called before snapshot
@register_before_snapshot
def download_llm_models():
    # Download an object from S3 and save to tmp
    # This files will persist in this snapshot
    with open('/tmp/FILE_NAME', 'wb') as f:
        s3_client.download_fileobj('amzn-s3-demo-bucket', 'OBJECT_NAME', f)
    ...

def lambda_handler(event, context):
    ...
```

### .NET
<a name="snapstart-tuning-dotnet"></a>

若要減少即時 (JIT) 編譯和組裝載入時間，請考慮從 `RegisterBeforeCheckpoint` [執行時期勾點](snapstart-runtime-hooks-dotnet.md)調用函數處理常式。由於 .NET 分層編譯的運作方式，多次調用處理常式可獲得最佳結果，如以下範例所示。

**重要**  
請確定虛擬函數調用不會產生非預期的副作用，例如啟動商業交易。

**Example**  

```
public class Function
{
    public Function()
    {
        Amazon.Lambda.Core.SnapshotRestore.RegisterBeforeSnapshot(FunctionWarmup);
    }

    // Warmup method that calls the function handler before snapshot to warm up the .NET code and runtime.
    // This speeds up future cold starts after restoring from a snapshot.

    private async ValueTask FunctionWarmup()
    {
        var request = new APIGatewayProxyRequest
        {
            Path = "/heathcheck",
            HttpMethod = "GET"
        };

        for (var i = 0; i < 10; i++)
        {
            await FunctionHandler(request, null);
        }
    }

    public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
    {
        //
        // Process HTTP request
        // 

        var response = new APIGatewayProxyResponse
        {
            StatusCode = 200
        };
        
        return await Task.FromResult(response);
    }
}
```

## 網路最佳實務
<a name="snapstart-networking"></a>

Lambda 從快照恢復函數時，無法保證函數在初始化階段建立的連線狀態。在大多數情況下， AWS 軟體開發套件建立的網路連線會自動恢復。針對其他連線，建議遵循最佳實務操作。

**重新建立網路連線**  
函數從快照恢復時，請務必重新建立網路連線。建議您在函數處理常式中重新建立網路連線。您也可以使用還原後[執行時期勾點](snapstart-runtime-hooks.md)。

**請勿使用主機名做為唯一的執行環境識別符**  
建議您不要使用 `hostname` 將執行環境識別為應用程式中唯一的節點或容器。對於 SnapStart，會使用單一快照做為多個執行環境的初始狀態。所有執行環境對 `InetAddress.getLocalHost()` (Java)、`socket.gethostname()` (Python) 和 `Dns.GetHostName()` (.NET) 都會傳回相同的 `hostname` 值。如果應用程式需要唯一的執行環境識別或 `hostname` 值，建議您在函數處理常式中產生唯一的 ID。或者，使用還原後[執行時期勾點](snapstart-runtime-hooks.md)來產生唯一的 ID，然後使用這個唯一 ID 做為執行環境的識別符。

**避免將連線繫結至固定來源連接埠**  
建議您避免將網路連線繫結至固定來源連接埠。函數從快照恢復時連線會重新建立，而繫結至固定來源連接埠的網路連線可能會失敗。

**避免使用 Java DNS 快取**  
Lambda 函數已快取 DNS 回應。如果您透過 SnapStart 使用其他 DNS 快取，則當函數從快照恢復，可能會發生連線逾時的情況。

`java.util.logging.Logger` 類別可以間接啟用 JVM DNS 快取。若要覆寫預設設定，請先將 [networkaddress.cache.ttl](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/InetAddress.html#inetaddress-caching-heading) 設定為 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);
}
```

為防止 Java 11 執行時期出現 `UnknownHostException` 失敗，建議將 `networkaddress.cache.negative.ttl` 設定為 0。在 Java 17 和更新版本的執行時期中，則不需要此步驟。您可以使用 `AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0` 環境變數為 Lambda 函數設定此屬性。

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