

第 4 版 (V4) 適用於 .NET 的 AWS SDK 已發行！

如需有關中斷變更和遷移應用程式的資訊，請參閱[遷移主題](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html)。

 [![Orange button with text "Click here for details".](https://docs.aws.amazon.com/zh_tw/sdk-for-net/v4/developer-guide/images/BannerButton_less-round.png)](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html)

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

# 的效能最佳實務 適用於 .NET 的 AWS SDK
<a name="net-dg-performance"></a>

建立用戶端、處理回應和設定應用程式的方式，會對輸送量、延遲和記憶體使用量產生很大的影響。本主題說明可協助應用程式有效且可靠地執行的用量和組態模式。這些模式在高負載或資源受限的環境中最為重要，例如容器和無伺服器函數。遵循這些實務可以改善效能，並防止常見問題，例如回應緩慢、懸置和高記憶體使用量。

最具影響力的做法如下：
+ [重複使用單一、長期的服務用戶端，](#net-dg-performance-reuse-clients)而不是為每個請求建立一個。
+ [處置回應和串流](#net-dg-performance-dispose)，以便將網路連線釋放回連線集區。
+ [正確使用 `async`/`await`](#net-dg-performance-async) 且絕不會封鎖非同步 SDK 呼叫。
+ 為 AWS Lambda 和 Amazon ECS 等受限環境[設定 .NET 垃圾回收](#net-dg-performance-gc)。
+ 在高輸送量下[管理 HTTP 連線](#net-dg-performance-connections)和連線限制。

開始之前，請確定您已[設定環境](net-dg-config.md)並[設定專案](configuring-the-sdk.md)。

## 重複使用單一、長期的服務用戶端
<a name="net-dg-performance-reuse-clients"></a>

[AmazonS3Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TS3Client.html) 和 [AmazonDynamoDBClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/TDynamoDBClient.html) 等服務用戶端具有*執行緒安全性*，建構成本相對*較高*，且*壽命較長*。建構用戶端可解析區域和端點資訊，並建立基礎 HTTP 基礎設施。為每個服務建立一個用戶端，並在應用程式的生命週期內重複使用它。如果您呼叫多個區域，請為每個區域建立單獨的用戶端。

**警告**  
請勿為每個請求或在迴圈中建立新的服務用戶端。建立用戶端會重複流失基礎 HTTP 基礎設施，這會增加可測量的延遲。它也可以耗盡通訊端或處理，並導致請求在載入時失敗或停滯。

在使用相依性插入的應用程式中，將用戶端註冊為單一用戶端。`AWSSDK.Extensions.NETCore.Setup` NuGet 套件的`AddAWSService`延伸方法會將用戶端註冊為預設的 生命週期`ServiceLifetime.Singleton`。用戶端會在第一次請求時建立，並在程序的生命週期內重複使用相同的執行個體。如需使用相依性注入註冊 AWS 服務以及從組態讀取選項的詳細資訊，請參閱 [AWSSDK.Extensions.NETCore.Setup 和 IConfiguration](net-dg-config-netcore.md)。

您也可以手動將用戶端註冊為單一用戶端。

```
builder.Services.AddSingleton<IAmazonS3>(_ => new AmazonS3Client());
```

**注意**  
由於 預設會將用戶端`AddAWSService`註冊為單一用戶端，因此請勿處置其提供的用戶端。如果您需要非預設生命週期，請將不同的`ServiceLifetime`值傳遞給 的選用`lifetime`參數`AddAWSService`。

重複使用用戶端**並不**表示您應該停止處理每個操作的回應。在應用程式的生命週期內重複使用用戶端，但繼續處置個別操作傳回的回應和串流，如 中所述[處置回應和串流](#net-dg-performance-dispose)。

## 處置回應和串流以釋出連線
<a name="net-dg-performance-dispose"></a>

有些 SDK 回應物件具有即時網路串流。最常見的範例是 [GetObjectResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TGetObjectResponse.html)，它會透過其`ResponseStream`屬性實作`IDisposable`和公開物件的內容。回應會保留開放的 HTTP 連線，直到串流完全讀取或回應遭到處置為止。如果您洩漏這些回應 （例如，透過在迴圈`GetObject`中呼叫而不捨棄每個結果），開啟的連線會累積到連線集區耗盡為止，以及下一個呼叫區塊。這是「隨機」或停止在 Nth 物件上的下載報告背後的原因。

一律將串流回應包裝在`using`陳述式中，並立即讀取或複製其串流。

```
using Amazon.S3;
using Amazon.S3.Model;

// s3Client is a reused, long-lived client.
var request = new GetObjectRequest { BucketName = bucketName, Key = key };

using var response = await s3Client.GetObjectAsync(request);
await response.WriteResponseStreamToFileAsync(filePath, append: false, CancellationToken.None);
```

處置回應並處置其`ResponseStream`相當；其中一個會關閉基礎網路串流，並傳回與集區的連線。

**提示**  
如果您只需要物件中繼資料，例如大小、上次修改的時間、內容類型或 ETag，請呼叫 `GetObjectMetadata`或 ，`GetObjectMetadataAsync`而非 `GetObject`。中繼資料操作會發出 HTTP `HEAD`請求，且不會傳輸任何物件內文，因此沒有要管理的內容串流。  

```
var metadata = await s3Client.GetObjectMetadataAsync(bucketName, key);
Console.WriteLine($"Size: {metadata.ContentLength} bytes");
```

**警告**  
回應和串流類型，例如 `GetObjectResponse`不會實作定稿器，因此您無法依賴垃圾收集來為您釋出其連線。您必須使用 `using`或明確`Dispose`呼叫來決定性地處置回應和串流。

## 正確使用非同步/等待
<a name="net-dg-performance-async"></a>

在現代 .NET 上， 中的服務操作 適用於 .NET 的 AWS SDK 是非同步的，並傳回 `Task`。在 .NET Framework 上，同步方法也存在，但建議使用更好的非同步呼叫擴展。使用 操作`await`，並將程式碼`async`的每一層傳播到進入點。如需使用 SDK 進行非同步程式設計的詳細資訊，請參閱 [非同步程式設計](sdk-net-async-api.md)。

**警告**  
請勿以 `.Result`、 `.Wait()`或 封鎖非同步 SDK 呼叫`.GetAwaiter().GetResult()`。此*sync-over-async*模式是應用程式停止運作的常見原因：  
在負載下，封鎖呼叫會耗用比執行緒集區成長更快的執行緒，因此無法執行接續。此*執行緒集區匱乏*顯示為無限期停止，是現代 .NET 上的主要失敗模式 （其中 ASP.NET Core 沒有預設的 `SynchronizationContext`)。
有些內容會擷取 `SynchronizationContext`，例如傳統 ASP.NET、Windows Forms、Blazor WebAssembly、 WPF和 .NET Framework 應用程式。在這些內容中，封鎖呼叫執行緒，而接續需要相同的執行緒產生*死鎖*。開發套件中的操作會在`ConfigureAwait(false)`內部使用 ，因此不會將其接續發佈回擷取的內容。死結源自於您呼叫鏈中其他位置擷取死結的`async`程式碼。`await` 在整個過程中使用 SDK 呼叫可完全避免問題發生。

下列方法會在非同步呼叫上封鎖 ，並可能使執行緒集區死結或挹注。

```
// Anti-pattern: do not do this.
public GetObjectResponse Get(GetObjectRequest request)
{
    return s3Client.GetObjectAsync(request).Result;
}
```

請改為進行 方法`async`和 `await` 呼叫。

```
public async Task<GetObjectResponse> GetAsync(GetObjectRequest request)
{
    return await s3Client.GetObjectAsync(request);
}
```

非同步程式碼的其他建議：
+ 將 傳遞`CancellationToken`至每個 操作，以便可以取消緩慢或停滯的呼叫，而不是掛斷。如需詳細資訊，請參閱[針對逾時使用 `CancellationToken` 參數](retries-timeouts.md#timeouts-async)。
+ 請勿在空白`catch`區塊中吞下例外狀況。這樣做會隱藏實際故障，並使懸置與錯誤不區分。擷取特定例外狀況並加以記錄。
+ 還原擷取的例外狀況時，請使用 `throw;`而非 ，`throw ex;`以保留原始堆疊追蹤。
+ 如果您必須從同步界限呼叫 ，請將它視為最後手段，並將工作與擷取的內容隔離，而不是讓封鎖呼叫成為預設模式。

## 設定 AWS Lambda 和 Amazon ECS 的 .NET 垃圾回收
<a name="net-dg-performance-gc"></a>

當容器出現「記憶體洩漏」時，.NET 垃圾收集器 (GC) 可能會保留回收的記憶體以供重複使用。因此，即使受管堆積未增長，程序記憶體也會顯示高且穩定。此外，GC 不會自動偵測容器的記憶體限制 （其 cgroup 限制）。在受限的環境中，堆積可能會增加到主機的記憶體，而不是容器的限制。這可能會導致 `OutOfMemoryException`或容器終止。

為了協助 GC 在受限的環境中正常運作：
+ 在容器上設定明確的記憶體限制，讓 GC 遵守 cgroup 限制，和/或設定 `DOTNET_GCHeapHardLimit`（絕對位元組值，以十六進位為單位） 或`DOTNET_GCHeapHardLimitPercent`環境變數來限制受管堆積。在數個報告的 Amazon ECS out-of-memory案例中，設定硬記憶體限制可解決當機問題。
+ 在小型 AWS Lambda 和 Amazon ECS 主機上，請考慮停用並行 （背景） 垃圾收集，讓收集器不會保留額外的記憶體；例如，將`DOTNET_gcConcurrent`環境變數設定為 `0`，或在`<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>`專案檔案中設定 。
+ 繫結並行。一次啟動許多操作，例如`Task.WhenAll`透過大型集合呼叫 ，會膨脹程序記憶體，並可能使連線集區停止運作。改為限制平行處理的程度。例如，避免此無限制模式：

  ```
  // Anti-pattern: starts one task per item with no limit.
  await Task.WhenAll(keys.Select(key => s3Client.GetObjectMetadataAsync(bucket, key)));
  ```

  反之，上限會與 並行`Parallel.ForEachAsync`：

  ```
  var options = new ParallelOptions { MaxDegreeOfParallelism = 10 };
  await Parallel.ForEachAsync(keys, options, async (key, token) =>
  {
      await s3Client.GetObjectMetadataAsync(bucket, key, token);
  });
  ```

如需這些設定的詳細資訊，請參閱 [https：// 上的垃圾回收執行時間組態選項](https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector)。 learn.microsoft.com 如需 AWS 運算的特定指引，請參閱 AWS 開發人員工具部落格文章為 [ Amazon ECS 和 設定 .NET 垃圾回收 AWS Lambda](https://aws.amazon.com/blogs/developer/configuring-net-garbage-collection-for-amazon-ecs-and-aws-lambda)。

## 管理 HTTP 連線和連線限制
<a name="net-dg-performance-connections"></a>

在高輸送量下，兩個與連線相關的問題很常見。第一個是開啟太多短期連線。這會耗盡暫時性連接埠、在 中保留通訊端`TIME_WAIT`，並新增 TCP 和 TLS 交握延遲。第二個連線數量太少，造成平行處理瓶頸。重複使用單一長期用戶端 （請參閱[重複使用服務用戶端](#net-dg-performance-reuse-clients)) 是正常運作連線集區的基礎，因為集區取決於要重複使用的用戶端。

若要調整每個端點的並行連線數，請在用戶端組態上設定 `MaxConnectionsPerServer` 屬性。當此屬性為 `null`（預設值） 時，會套用基礎`HttpClientHandler`預設值，這實際上在現代 .NET 上不受限制。只有當對相同端點的許多並行請求在連線上遇到瓶頸時，才會引發此錯誤。良好的起點是每個端點預期的並行請求尖峰數量。將其設定為遠高於工作負載需要浪費通訊端，而不會改善輸送量。

```
using Amazon.S3;

var config = new AmazonS3Config
{
    MaxConnectionsPerServer = 50
};

var s3Client = new AmazonS3Client(config);
```

如果您已使用相依性插入，請透過 設定您的用戶端`AWSSDK.Extensions.NETCore.Setup`。當您使用 DI 或註冊數個服務用戶端時，建議使用此方法。它集中了組態，並使用戶端易於注入和測試。您可以從應用程式的組態設定組態值，而不是在程式碼中設定。如需詳細資訊，請參閱[AWSSDK.Extensions.NETCore.Setup 和 IConfiguration](net-dg-config-netcore.md)。

最後，將自己的平行處理綁定，這樣您就不會啟動比連線限制允許的更多並行操作。例如，大小符合您連線限制`SemaphoreSlim`的閘道呼叫：

```
var throttle = new SemaphoreSlim(50); // match MaxConnectionsPerServer
await throttle.WaitAsync(token);
try
{
    await s3Client.GetObjectAsync(request, token);
}
finally
{
    throttle.Release();
}
```

## 設定逾時和重試
<a name="net-dg-performance-timeouts"></a>

逾時和重試對感知效能有直接影響。值`Timeout`太高可讓停滯的請求區塊長時間。當服務已傳回限流錯誤時，積極的重試政策會新增更多請求，並可能使限流惡化。選擇符合您應用程式延遲與失敗容錯能力的重試政策，並讓真正的例外狀況傳播，而不是以遮罩懸置的方式重試。

**注意**  
`Timeout` 屬性不會影響非同步呼叫。如果您使用的是非同步呼叫，請[針對逾時使用 `CancellationToken` 參數](retries-timeouts.md#timeouts-async)改為參閱 。

如需重試模式、 `MaxErrorRetry`和 `Timeout` 屬性 (`ReadWriteTimeout` 僅適用於 .NET Framework) 的詳細資訊，以及如何設定這些模式的範例，請參閱 [重試和逾時](retries-timeouts.md)。

## 最佳化串流和大型物件傳輸 (Amazon S3)
<a name="net-dg-performance-streaming"></a>

若要上傳和下載大型物件或多個物件，請使用 `Amazon.S3.Transfer` 命名空間中的 [TransferUtility](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TTransferUtility.html) 類別。它使用分段傳輸並行上傳和下載，並為您管理串流、組件和連線。這比單一串流傳輸更快，而且是移動大型物件的建議方式。
+ **平行分段下載。**舊版 SDK 會將物件下載為單一串流，而不是平行下載組件。從 4.0.17 `AWSSDK.S3`版開始， 會透過 `DownloadWithResponseAsync`、 `OpenStreamWithResponseAsync`和 `DownloadDirectoryWithResponseAsync`方法`TransferUtility`提供分段 （平行） 下載。當您使用 時`OpenStreamWithResponseAsync`，當您使用傳回的串流時，物件的組件會緩衝在記憶體中。使用 [TransferUtilityOpenStreamRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TTransferUtilityOpenStreamRequest.html) 的 `MaxInMemoryParts` 屬性控制緩衝的組件數量。對於大多數傳輸，偏好 `TransferUtility`。只有在您需要特定範圍或自訂平行處理方案時，才能使用 [GetObjectRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TGetObjectRequest.html) `ByteRange` 屬性自行下載位元組範圍。如需詳細資訊，請參閱《 開發人員工具部落格》中的 AWS [介紹適用於 .NET Transfer Manager 的 AWS SDK 的分段下載支援](https://aws.amazon.com/blogs/developer/introducing-multipart-download-support-for-aws-sdk-for-net-transfer-manager/)。
+ **上傳的內容長度。**Amazon S3 `PUT`需要已知的內容長度，而且 SDK 預設會計算請求內文上的檢查總和。當長度已知且可搜尋串流時，軟體開發套件可以執行此操作，而不會緩衝記憶體中的整個物件。會視需要緩衝，為您`TransferUtility`處理不可查看的串流。如果您`PutObjectAsync`直接呼叫不可查看的串流 （例如 中的原始請求內文ASP.NET Core)，則請求可能會失敗。提供可尋求的串流、明確設定內容長度，或設定 SDK 計算檢查總和的方式。如需詳細資訊，請參閱《 AWS SDKs和工具參考指南》中的[資料完整性保護](https://docs.aws.amazon.com/sdkref/latest/guide/feature-dataintegrity.html)。
+ **部分大小。**Amazon S3 每次分段上傳最多允許 10，000 個組件。當總長度已知時，開發套件會自動計算保持在此限制內的部分大小。您主要需要`PartSize`為長度無法事先得知的串流自行設定。否則，小型預設組件可能會超過非常大型上傳的限制。較大的組件大小也會降低每個組件的額外負荷，而成本是每個組件的更多記憶體。

## 診斷效能問題
<a name="net-dg-performance-diagnose"></a>

當您調查緩慢、懸置或明顯洩漏時，下列訊號可協助您快速找到原因：
+ 處於 `CLOSE_WAIT`或 `TIME_WAIT` 狀態的通訊端數量不斷增加 （以 可見`netstat`) 是未處置回應的指紋，或每個請求建立和捨棄的用戶端的指紋。請參閱 [處置回應和串流](#net-dg-performance-dispose) 和 [重複使用服務用戶端](#net-dg-performance-reuse-clients)。
+ 啟用請求指標和回應記錄，以確認請求確實正在分派，並測量延遲。在建立服務用戶端之前，在 [AWSConfigs](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Amazon/TAWSConfigs.html) 上設定 [LoggingConfig](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Util/TLoggingConfig.html) 物件的屬性。用戶端會擷取設定，例如建構`LogMetrics`時。

  ```
  using Amazon;
  
  AWSConfigs.LoggingConfig.LogMetrics = true;
  AWSConfigs.LoggingConfig.LogResponses = ResponseLoggingOption.OnError;
  ```
+ 調查記憶體時，請區分整個程序的記憶體與受管堆積。高但穩定的程序記憶體通常是 GC 保留回收記憶體，而不是洩漏。請參閱[設定垃圾回收](#net-dg-performance-gc)。