

 适用于 .NET 的 AWS SDK V3 已进入维护模式。

我们建议您迁移到 [适用于 .NET 的 AWS SDK V4](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/welcome.html)。有关如何迁移的更多详细信息和信息，请参阅我们的[维护模式公告](https://aws.amazon.com/blogs/developer/aws-sdk-for-net-v3-maintenance-mode-announcement/)。

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 适用于 .NET 的 AWS SDK 的功能
<a name="net-dg-sdk-features"></a>

本部分提供了有关在创建应用程序时可能需要考虑的适用于 .NET 的 AWS SDK功能的信息。

请务必先[设置项目](net-dg-config.md)。

有关为特定 AWS 服务开发软件及相关代码示例的信息，请参阅[使用 AWS 服务](working-with-aws-services.md)。有关其他代码示例，请参阅 [适用于 .NET 的 SDK 代码示例](csharp_code_examples.md)。

**Topics**
+ [异步 API](sdk-net-async-api.md)
+ [重试和超时](retries-timeouts.md)
+ [分页器](paginators.md)
+ [可观测性](observability.md)
+ [其他工具](sdk-features-additional-tools.md)

# AWS适用于 .NET 的 异步 API
<a name="sdk-net-async-api"></a>

适用于 .NET 的 AWS SDK使用*基于任务的异步模式（TAP）*进行异步实现。要了解有关 TAP 的更多信息，请参阅 docs.microsoft.com 上的[基于任务的异步模式（TAP）](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap)。

本主题概述了在调用 AWS 服务客户端时如何使用 TAP。

适用于 .NET 的 AWS SDK API 中的异步方法是基于 `Task` 类或 `Task<TResult>` 类的操作。有关以下这些类的信息，请参阅 docs.microsoft.com：[Task 类](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task)、[Task<TResult> 类](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1)。

在您的代码中调用这些 API 方法时，必须在使用 `async` 关键字声明的函数中调用，如以下示例所示。

```
static async Task Main(string[] args)
{
  ...
  // Call the function that contains the asynchronous API method.
  // Could also call the asynchronous API method directly from Main
  //  because Main is declared async
  var response = await ListBucketsAsync();
  Console.WriteLine($"Number of buckets: {response.Buckets.Count}");
  ...
}

// Async method to get a list of Amazon S3 buckets.
private static async Task<ListBucketsResponse> ListBucketsAsync()
{
  ...
  var response = await s3Client.ListBucketsAsync();
  return response;
}
```

如前面的代码片段所示，`async` 声明的首选范围是 `Main` 函数。设置此 `async` 范围可确保要求对 AWS 服务客户端的调用都是异步的。如果由于某种原因无法声明 `Main` 为异步，则可以在除 `Main` 之外的函数上使用 `async` 关键字，然后从那里调用 API 方法，如以下示例所示。

```
static void Main(string[] args)
{
  ...
  Task<ListBucketsResponse> response = ListBucketsAsync();
  Console.WriteLine($"Number of buckets: {response.Result.Buckets.Count}");
  ...
}

// Async method to get a list of Amazon S3 buckets.
private static async Task<ListBucketsResponse> ListBucketsAsync()
{
  ...
  var response = await s3Client.ListBucketsAsync();
  return response;
}
```

请注意使用此模式时 `Main` 所需的特殊 `Task<>` 语法。此外，您必须使用响应的 **`Result`** 成员来获取数据。

您可以在[快速了解](quick-start.md)部分（[简单跨平台应用程序](quick-start-s3-1-cross.md)和[基于 Windows 的简单应用程序](quick-start-s3-1-winvs.md)）和[带有指导的代码示例高级库和框架](tutorials-examples.md)中查看对 AWS 服务客户端进行异步调用的完整示例。

# 重试和超时
<a name="retries-timeouts"></a>

 适用于 .NET 的 AWS SDK 允许您为向 AWS 服务发出的 HTTP 请求配置重试次数和超时值。如果重试和超时的默认值不适用于您的应用程序，您可以针对具体要求调整这些值，但请务必了解这样做将对应用程序的行为带来什么影响。

在确定重试和超时值时，请考虑以下因素：
+ 当网络连接下降或 AWS 服务无法访问时， 适用于 .NET 的 AWS SDK 和您的应用程序应如何响应？ 您是希望调用快速失败，还是希望调用代表您不断重试？
+ 您的应用程序是必须能够及时响应的面向用户的应用程序或网站，还是更能容忍延迟增加的后台处理任务？
+ 应用程序是部署在具有低延迟的可靠网络上，还是部署在具有不可靠连接的远程位置？

## 重试
<a name="retries"></a>

### 概述
<a name="w2aac11c13c11b5"></a>

 适用于 .NET 的 AWS SDK 可以重试因服务器端限制或连接中断而失败的请求。服务配置类有两个属性可用于指定服务客户端的重试行为。服务配置类继承了抽象的 [Amazon.Runtime 中的这些属性。 ](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TClientConfig.html)ClientConfig[适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)的类别：
+ `RetryMode`指定三种重试模式之一，这些模式[在 Amazon.Runtime 中定义。 RequestRetryMode](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TRequestRetryMode.html)枚举。

  可以使用`AWS_RETRY_MODE`环境变量或共享 AWS 配置文件中的 *retry\$1mod* e 设置来控制应用程序的默认值。
+ `MaxErrorRetry` 指定服务客户端级别允许的重试次数；SDK 会在失败并引发异常之前按指定的次数重试该操作。

  可以使用`AWS_MAX_ATTEMPTS`环境变量或共享 AWS 配置文件中的 *max\$1* tempts 设置来控制应用程序的默认值。

这些属性的详细描述可以在摘要的 [Amazon.Runtime 中找到。 ](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TClientConfig.html)ClientConfig[适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)的类别。默认情况下，`RetryMode` 的每个值对应一个特定的 `MaxErrorRetry` 值，如下表所示。

|  |  |  | 
| --- |--- |--- |
| Legacy | 10 | 4 | 
| 标准 | 10 | 2 | 
| 自适应（实验性） | 10 | 2 | 

### 行为
<a name="w2aac11c13c11b9"></a>

**当应用程序启动时**

应用程序启动时，`RetryMode` 和 `MaxErrorRetry` 的默认值由 SDK 配置。除非您指定其它值，否则在创建服务客户端时将使用这些默认值。
+ 如果您的环境中未设置这些属性，则 `RetryMode` 的默认值将配置为 *Legacy*，而 `MaxErrorRetry` 的默认值将配置为上表中的相应值。
+ 如果您的环境中设置了重试模式，则该值将用作 `RetryMode` 的默认值。`MaxErrorRetry` 的默认值将配置为上表中的相应值，除非您的环境中也设置了最大错误的值（下文将介绍）。
+ 如果您的环境中设置了最大错误的值，则该值将用作 `MaxErrorRetry` 的默认值。Amazon DynamoDB 是该规则的例外；`MaxErrorRetry` 的默认 DynamoDB 值始终是上表中的值。

**当您的应用程序运行时**

创建服务客户端时，可以使用 `RetryMode` 和 `MaxErrorRetry` 的默认值（如前所述），也可以指定其它值。要指定其他值，请在创建服务客户端SQSConfig时创建并包含服务配置对象，例如[AmazonDynamoDBConfig](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/TDynamoDBConfig.html)或 [Amazon](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSConfig.html)。

一旦为服务客户端创建这些值，便不可更改。

**注意事项**

在进行重试时，会增加请求的延迟。您应根据应用程序对请求总延迟和错误率的限制来配置重试次数。

## 超时
<a name="timeouts"></a>

 适用于 .NET 的 AWS SDK 使您能够在服务客户端级别和每个方法调用配置请求超时。有两种配置超时的机制，后续章节将介绍这些机制：
+ 如果您使用的是[异步调用](#timeouts-async)，则可以使用该方法的 `CancellationToken` 参数。
+ 如果您[在.NET 框架中使用同步调用](#timeouts-sync-framework)，则可以使用抽象 [Amazon.Runtime 的`Timeout`和`ReadWriteTimeout`属性。 ClientConfig](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TClientConfig.html)班级。

### 使用 `CancellationToken` 参数配置超时
<a name="timeouts-async"></a>

 适用于 .NET 的 AWS SDK 允许您使用`CancellationToken`参数配置异步调用的请求超时。下面的代码片段展示了一个示例。如果请求未在 10 秒内完成，则代码会引发 `System.Threading.Tasks.TaskCanceledException`。

```
string bucketName = "amzn-s3-demo-bucket";
string path = "pathToBucket";
using (var amazonS3Client = new AmazonS3Client(new AmazonS3Config()))
{
    // Cancel request after 10 seconds
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(10000));
    CancellationToken cancellationToken = cancellationTokenSource.Token;
    ListObjectsV2Request listRequestV2 = new()
    {
        BucketName = bucketName,
        Prefix = path,
    };

    ListObjectsV2Response listResponseV2 = await amazonS3Client.ListObjectsV2Async(listRequestV2, cancellationToken);
}
```

### 使用 `Timeout` 和 `ReadWriteTimeout` 属性配置超时
<a name="timeouts-sync-framework"></a>

**注意**  
`Timeout` 属性不会影响异步调用。如果您使用的是异步调用，请参阅[使用 `CancellationToken` 参数配置超时](#timeouts-async)。

 适用于 .NET 的 AWS SDK 使您能够在服务客户端级别配置请求超时和套接字 read/write 超时值。这些值是在抽象的 [Amazon.Runtime 的`Timeout`和`ReadWriteTimeout`属性中指定的。 ClientConfig](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TClientConfig.html)班级。这些值作为 AWS 服务客户端[HttpWebRequest](https://learn.microsoft.com/en-us/dotnet/api/system.net.httpwebrequest)对象创建的对象的`Timeout`和`ReadWriteTimeout`属性传递。默认情况下，`Timeout` 值为 100 秒，`ReadWriteTimeout` 值为 300 秒。

当您的网络具有高延迟或存在导致操作重试的情况时，使用较长的超时值和较高的重试次数会导致一些开发工具包操作看上去没有响应。

**注意**  
以便携式类库 (PCL) 为目标的版本使用该[HttpClient](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient)类而不是`HttpWebRequest`类，并且仅支持 [Timeout](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout) 属性。 适用于 .NET 的 AWS SDK 

以下是默认超时值的例外情况。明确设置超时值时将覆盖这些值。
+ `Timeout`如果被调用的方法上传直播（例如 [Amazons3Client），则设置为最大值。`ReadWriteTimeout` PutObjectAsync()](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/MS3PutObjectAsyncPutObjectRequestCancellationToken.html)，[Amazons3Client。 UploadPartAsync()](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/MS3UploadPartAsyncUploadPartRequestCancellationToken.html)，[AmazonGlacierClient。 UploadArchiveAsync()](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Glacier/MGlacierUploadArchiveAsyncUploadArchiveRequestCancellationToken.html)，依此类推。
+  适用于 .NET 的 AWS SDK 该目标.NET 框架的版本设置为所有 [Amazons3Clien](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TS3Client.html) t `Timeout` 和`ReadWriteTimeout`对象的最大值。[AmazonGlacierClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Glacier/TGlacierClient.html)
+ 针对便携式类库 (PCL) 和.NET Core 的版本设置`Timeout`为所有 [Amazons3](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TS3Client.html) Client 和对象的最大值。 适用于 .NET 的 AWS SDK [AmazonGlacierClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Glacier/TGlacierClient.html)

以下示例说明如何指定*标准*重试模式、最多 3 次重试、10 秒超时和 10 秒 read/write 超时（如果适用）。[Amazons3Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TS3Client.html) 构造函数被赋予一个 [AmazonS3Config](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TS3Config.html) 对象。

```
var s3Client = new AmazonS3Client(
  new AmazonS3Config
  {
    Timeout = TimeSpan.FromSeconds(10),
    // NOTE: The following property is obsolete for
    //       versions of the 适用于 .NET 的 AWS SDK that target .NET Core.
    ReadWriteTimeout = TimeSpan.FromSeconds(10),
    RetryMode = RequestRetryMode.Standard,
    MaxErrorRetry = 3
  });
```

# 分页器
<a name="paginators"></a>

某些 AWS 服务会收集和存储大量数据，您可以使用适用于 .NET 的 AWS SDK的 API 调用来检索这些数据。如果您要检索的数据量变得太大，无法进行单个 API 调用，则可以通过使用*分页*将结果分成更易于管理的多个部分。

为了使您能够执行分页，开发工具包中许多服务客户端的请求和响应对象都提供了*延续令牌*（通常名为 `NextToken`）。其中一些服务客户端还提供了“分页工具”****。

分页工具使您能够避免延续令牌的开销，这可能涉及循环、状态变量、多个 API 调用等。使用分页工具时，您可以通过一行代码（`foreach` 循环的声明）从 AWS 服务中检索数据。如果需要多个 API 调用来检索数据，则分页工具会为您处理。

## 我在哪里可以找到分页工具？
<a name="paginators-find-them"></a>

并非所有服务都提供分页工具。确定服务是否为特定 API 提供分页工具的一种方法是在[适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)中查看服务客户端类的定义。

例如，如果您查看 [AmazonCloudWatchLogsClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudWatchLogs/TCloudWatchLogsClient.html) 类的定义，就会看到一个 `Paginators` 属性。该属性为 Amazon CloudWatch Logs 提供了分页工具。

## 分页工具能提供什么好处？
<a name="paginators-use-them"></a>

分页工具包含让您能够查看完整响应的属性。它们通常还包含一个或多个属性，使您可以访问响应中最有趣的部分，我们将其称为*关键结果*。

例如，在前面提到的 `AmazonCloudWatchLogsClient` 中，`Paginator` 对象包含一个 `Responses` 属性，其中包含来自 API 调用的完整 DescribeLogGroupsResponse 对象。此 `Responses` 属性包含日志组集合等内容。

Paginator 对象还包含一个名为 `LogGroups` 的关键结果。此属性仅包含响应的日志组部分。有了这个关键结果，您就能在许多情况下减少和简化代码。

## 同步分页与异步分页
<a name="paginators-sync-async"></a>

分页工具提供同步和异步分页机制。.NET Framework 4.7.2（或更高版本）项目中支持同步分页。异步分页可在 .NET Core 项目（.NET Core 3.1、.NET 5 等）中使用。

由于建议使用异步操作和 .NET Core，因此接下来的示例向您展示了异步分页。[分页工具的其它注意事项](#paginators-additional)中的示例后面显示了有关如何使用同步分页和 .NET Framework 4.7.2（或更高版本）执行相同任务的信息。

## 示例
<a name="paginators-example"></a>

以下示例为您展示了如何使用适用于 .NET 的 AWS SDK显示日志组列表。相比之下，该示例说明如何使用和不使用分页工具执行该操作。在查看完整代码（稍后所示）之前，请考虑以下片段。

**在不使用分页工具的情况下获取 CloudWatch 日志组**

```
      // Loop as many times as needed to get all the log groups
      var request = new DescribeLogGroupsRequest{Limit = LogGroupLimit};
      do
      {
        Console.WriteLine($"Getting up to {LogGroupLimit} log groups...");
        var response = await cwClient.DescribeLogGroupsAsync(request);
        foreach(var logGroup in response.LogGroups)
        {
          Console.WriteLine($"{logGroup.LogGroupName}");
        }
        request.NextToken = response.NextToken;
      } while(!string.IsNullOrEmpty(request.NextToken));
```

**使用分页工具获取 CloudWatch 日志组**

```
      // No need to loop to get all the log groups--the SDK does it for us behind the scenes
      var paginatorForLogGroups =
        cwClient.Paginators.DescribeLogGroups(new DescribeLogGroupsRequest());
      await foreach(var logGroup in paginatorForLogGroups.LogGroups)
      {
        Console.WriteLine(logGroup.LogGroupName);
      }
```

这两个片段的结果完全相同，因此可以清楚地看到使用分页工具的好处。

**注意**  
在尝试构建和运行完整代码之前，请确保已[设置环境和项目](net-dg-config.md)。  
您可能还需要 [Microsoft.Bcl.AsyncInterfaces](https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces/) NuGet 程序包，因为异步分页工具使用 `IAsyncEnumerable` 接口。

### 完整代码
<a name="paginators-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

#### SDK 参考
<a name="w2aac11c15c23c19b5b1"></a>

NuGet 程序包：
+ [AWSSDK.CloudWatch](https://www.nuget.org/packages/AWSSDK.CloudWatch)

编程元素：
+ 命名空间 [Amazon.CloudWatch](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudWatch/NCloudWatch.html)

  类 [AmazonCloudWatchLogsClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudWatchLogs/TCloudWatchLogsClient.html)
+ 命名空间 [Amazon.CloudWatchLogs.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudWatchLogs/NCloudWatchLogsModel.html)

  类 [DescribeLogGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudWatchLogs/TDescribeLogGroupsRequest.html)

  类 [DescribeLogGroupsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudWatchLogs/TDescribeLogGroupsResponse.html)

  类 [LogGroup](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudWatchLogs/TLogGroup.html)

#### 完整代码
<a name="w2aac11c15c23c19b7b1"></a>

```
using System;
using System.Threading.Tasks;
using Amazon.CloudWatchLogs;
using Amazon.CloudWatchLogs.Model;

namespace CWGetLogGroups
{
  class Program
  {
    // A small limit for demonstration purposes
    private const int LogGroupLimit = 3;

    //
    // Main method
    static async Task Main(string[] args)
    {
      var cwClient = new AmazonCloudWatchLogsClient();
      await DisplayLogGroupsWithoutPaginators(cwClient);
      await DisplayLogGroupsWithPaginators(cwClient);
    }


    //
    // Method to get CloudWatch log groups without paginators
    private static async Task DisplayLogGroupsWithoutPaginators(IAmazonCloudWatchLogs cwClient)
    {
      Console.WriteLine("\nGetting list of CloudWatch log groups without using paginators...");
      Console.WriteLine("------------------------------------------------------------------");

      // Loop as many times as needed to get all the log groups
      var request = new DescribeLogGroupsRequest{Limit = LogGroupLimit};
      do
      {
        Console.WriteLine($"Getting up to {LogGroupLimit} log groups...");
        DescribeLogGroupsResponse response = await cwClient.DescribeLogGroupsAsync(request);
        foreach(LogGroup logGroup in response.LogGroups)
        {
          Console.WriteLine($"{logGroup.LogGroupName}");
        }
        request.NextToken = response.NextToken;
      } while(!string.IsNullOrEmpty(request.NextToken));
    }


    //
    // Method to get CloudWatch log groups by using paginators
    private static async Task DisplayLogGroupsWithPaginators(IAmazonCloudWatchLogs cwClient)
    {
      Console.WriteLine("\nGetting list of CloudWatch log groups by using paginators...");
      Console.WriteLine("-------------------------------------------------------------");

      // Access the key results; i.e., the log groups
      // No need to loop to get all the log groups--the SDK does it for us behind the scenes
      Console.WriteLine("\nFrom the key results...");
      Console.WriteLine("------------------------");
      IDescribeLogGroupsPaginator paginatorForLogGroups =
        cwClient.Paginators.DescribeLogGroups(new DescribeLogGroupsRequest());
      await foreach(LogGroup logGroup in paginatorForLogGroups.LogGroups)
      {
        Console.WriteLine(logGroup.LogGroupName);
      }

      // Access the full response
      // Create a new paginator, do NOT reuse the one from above
      Console.WriteLine("\nFrom the full response...");
      Console.WriteLine("--------------------------");
      IDescribeLogGroupsPaginator paginatorForResponses =
        cwClient.Paginators.DescribeLogGroups(new DescribeLogGroupsRequest());
      await foreach(DescribeLogGroupsResponse response in paginatorForResponses.Responses)
      {
        Console.WriteLine($"Content length: {response.ContentLength}");
        Console.WriteLine($"HTTP result: {response.HttpStatusCode}");
        Console.WriteLine($"Metadata: {response.ResponseMetadata}");
        Console.WriteLine("Log groups:");
        foreach(LogGroup logGroup in response.LogGroups) 
        {
          Console.WriteLine($"\t{logGroup.LogGroupName}");
        }
      }
    }
  }
}
```

## 分页工具的其它注意事项
<a name="paginators-additional"></a>
+ **分页工具不能多次使用**

  如果您需要在代码的多个位置显示特定 AWS 分页工具的结果，则不得多次使用分页工具对象。相反，每次需要时都要创建一个新的分页工具。这个概念如 `DisplayLogGroupsWithPaginators` 方法前面的示例代码中所示。
+ **同步分页**

  同步分页适用于 .NET Framework 4.7.2（或更高版本）的项目。
**警告**  
自 2024 年 8 月 15 日起，适用于 .NET 的 AWS SDK 将终止对 .NET Framework 3.5 的支持，并将支持的 .NET Framework 最低版本更改为 4.7.2。有关更多信息，请参阅博客文章 [Important changes coming for .NET Framework 3.5 and 4.5 targets of the 适用于 .NET 的 AWS SDK](https://aws.amazon.com/blogs/developer/important-changes-coming-for-net-framework-3-5-and-4-5-targets-of-the-aws-sdk-for-net/)。

  要查看此内容，请创建一个 .NET Framework 4.7.2（或更高版本）项目，然后将前面的代码复制到该项目中。然后只需将 `await` 关键字从两个 `foreach` 分页工具调用中移除，如以下示例所示。

  ```
  /*await*/ foreach(var logGroup in paginatorForLogGroups.LogGroups)
  {
    Console.WriteLine(logGroup.LogGroupName);
  }
  ```

  生成并运行该项目，以查看与异步分页相同的结果。

# 可观测性
<a name="observability"></a>

可观测性是指可以根据系统发出的数据推断出其当前状态的程度。发出的数据通常称为遥测。

 适用于 .NET 的 AWS SDK 可以提供两种常见的遥测信号、指标和轨迹以及日志记录。您可以连接[TelemetryProvider](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TTelemetryProvider.html)以将遥测数据发送到可观测性后端（例如或 [AWS X-Ray](https://docs.aws.amazon.com/xray/?icmpid=docs_homepage_devtools)A [ma](https://docs.aws.amazon.com/cloudwatch/?icmpid=docs_homepage_mgmtgov) zon CloudWatch），然后对其进行操作。

默认情况下，SDK 中的遥测信号处于禁用状态。本主题介绍如何启用和配置遥测输出。

## 其他资源
<a name="observability-resources"></a>

有关启用和使用可观测性的更多信息，请参阅以下资源：
+ [OpenTelemetry](https://opentelemetry.io/)
+ 博客文章《[增强可观察性 适用于 .NET 的 AWS SDK 》 OpenTelemetry](https://aws.amazon.com/blogs/developer/enhancing-observability-in-the-aws-sdk-for-net-with-opentelemetry/)
+ 博客文章[宣布 AWS .NET OpenTelemetry 库正式上市](https://aws.amazon.com/blogs/dotnet/announcing-the-general-availability-of-aws-net-opentelemetry-libraries/)。
+ [的出口商 OpenTelemetry](https://opentelemetry.io/docs/languages/net/exporters/)
+ 有关可观测性的示例 AWS Tools for PowerShell，请参阅《[ PowerShell 用户工具](https://docs.aws.amazon.com/powershell/latest/userguide/)指南》中的[可观察性](https://docs.aws.amazon.com/powershell/latest/userguide/observability.html)。

## 配置 `TelemetryProvider`
<a name="observability-conf-telemetry-provider"></a>

您可以在应用程序中为所有服务客户端或单个客户端全局配置 `TelemetryProvider`，如以下示例所示。[遥测提供程序](observability-telemetry-providers.md)一节提供了有关遥测实现的信息，包括有关随 SDK 提供的实现的信息。

### 配置默认的全局遥测提供程序
<a name="observability-conf-telemetry-provider-global"></a>

默认情况下，每个服务客户端都尝试使用全局可用的遥测提供程序。这样，您只需设置一次提供程序，所有客户端都会使用它。此操作只需执行一次，且必须在创建任何服务客户端之前进行。

以下代码片段展示了如何设置全局遥测提供程序。然后，它会创建一个 Amazon S3 服务客户端，并尝试执行失败的操作。该代码向应用程序添加追踪记录和指标。此代码使用以下 NuGet 软件包：`OpenTelemetry.Exporter.Console`和`OpenTelemetry.Instrumentation.AWS`。

**注意**  
如果您使用身份 AWS IAM Identity Center 验证，请务必同时添加`AWSSDK.SSO`和`AWSSDK.SSOOIDC`。

```
using Amazon.S3;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

Sdk.CreateTracerProviderBuilder()
    .ConfigureResource(e => e.AddService("DemoOtel"))
    .AddAWSInstrumentation()
    .AddConsoleExporter()
    .Build();

Sdk.CreateMeterProviderBuilder()
    .ConfigureResource(e => e.AddService("DemoOtel"))
    .AddAWSInstrumentation()
    .AddConsoleExporter()
    .Build();

var s3Client = new AmazonS3Client();

try
{
    var listBucketsResponse = await s3Client.ListBucketsAsync();
    // Attempt to delete a bucket that doesn't exist.
    var deleteBucketResponse = await s3Client.DeleteBucketAsync("amzn-s3-demo-bucket");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Console.Read();
```

### 为特定服务客户端配置遥测提供程序
<a name="observability-conf-telemetry-provider-client"></a>

您可以为单个服务客户端配置特定的遥测提供程序（而不是全局遥测提供程序）。为此，请使用服务客户端构造函数的 Config 对象的 `TelemetryProvider` 类。例如，请参阅 [AmazonS3Config](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TS3Config.html) 并找到 `TelemetryProvider` 属性。有关自定义遥测实现的信息，请参阅[遥测提供程序](observability-telemetry-providers.md)。

**Topics**
+ [其他资源](#observability-resources)
+ [配置 `TelemetryProvider`](#observability-conf-telemetry-provider)
+ [指标](observability-metrics.md)
+ [遥测提供程序](observability-telemetry-providers.md)

# 指标
<a name="observability-metrics"></a>

下表列出了 SDK 发出的遥测指标。[配置遥测提供程序](observability.md#observability-conf-telemetry-provider)以使指标可观测。


**发出哪些指标？**  

| 指标名称 | 单位 | 类型 | 属性 | 描述 | 
| --- | --- | --- | --- | --- | 
| client.call.duration | s | 直方图 | rpc.service, rpc.method | 总体调用时长（包括重试时间以及发送或接收请求和响应正文所用的时间） | 
| client.uptime | s | 直方图 | rpc.service | 自客户端创建以来的时间 | 
| client.call.attempts | \$1attempt\$1 | MonotonicCounter | rpc.service, rpc.method | 单个操作的尝试次数 | 
| client.call.errors | \$1error\$1 | MonotonicCounter | rpc.service, rpc.method, exception.type | 操作的错误数 | 
| client.call.attempt\$1duration | s | 直方图 | rpc.service, rpc.method | 连接到服务、发送请求以及获取 HTTP 状态代码和标头所用的时间（包括等待发送的排队时间） | 
| client.call.resolve\$1endpoint\$1duration | s | 直方图 | rpc.service, rpc.method | 解析请求的端点（端点解析器，不是 DNS）所用的时间 | 
| client.call.serialization\$1duration | s | 直方图 | rpc.service, rpc.method | 序列化消息正文所用的时间 | 
| client.call.deserialization\$1duration | s | 直方图 | rpc.service, rpc.method | 反序列化消息正文所用的时间 | 
| client.call.auth.signing\$1duration | s | 直方图 | rpc.service, rpc.method | 签署请求所用的时间 | 
| client.call.auth.resolve\$1identity\$1duration | s | 直方图 | rpc.service, rpc.method | 从身份提供商处获取身份（例如 AWS 凭证或持有者令牌）所用的时间 | 
| client.http.bytes\$1sent | 方式 | MonotonicCounter | server.address | HTTP 客户端发送的字节总数 | 
| client.http.bytes\$1received | 方式 | MonotonicCounter | server.address | HTTP 客户端接收的字节总数 | 

以下是列说明：
+ **指标名称** – 发出的指标的名称。
+ **单位** - 指标的度量单位。单位以 [UCUM](https://unitsofmeasure.org/ucum) 区分大小写(“c/s”)表示法给出。
+ **类型** - 用于捕获指标的工具类型。
+ **属性** - 与指标一起发出的一组属性（维度）。
+ **描述** - 对指标所衡量内容的描述。

# 遥测提供程序
<a name="observability-telemetry-providers"></a>

该 SDK 提供了 [OpenTelemetry](https://opentelemetry.io/) 实现作为遥测提供程序，[下一节](observability-telemetry-providers-otel.md)将对此进行介绍。

如果您有特定的遥测要求、已构思好遥测解决方案，或者需要精细控制遥测数据的捕获和处理方式，则也可以实现自己的遥测提供程序。

在 [TelemetryProvider](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TTelemetryProvider.html) 类中注册您自己的实现。以下是一个简单的示例，展示了如何注册自己的 `TracerProvider` 和 `MeterProvider`。

```
using Amazon;
using Amazon.Runtime.Telemetry;
using Amazon.Runtime.Telemetry.Metrics;
using Amazon.Runtime.Telemetry.Tracing;

public class CustomTracerProvider : TracerProvider
{
    // Implement custom tracing logic here
}
public class CustomMeterProvider : MeterProvider
{
    // Implement custom metrics logic here
}

// Register custom implementations
AWSConfigs.TelemetryProvider.RegisterTracerProvider(new CustomTracerProvider());
AWSConfigs.TelemetryProvider.RegisterMeterProvider(new CustomMeterProvider());
```

**Topics**
+ [OpenTelemetry](observability-telemetry-providers-otel.md)

# 配置 OpenTelemetry基于遥测的提供商
<a name="observability-telemetry-providers-otel"></a>

 适用于 .NET 的 AWS SDK 包括 OpenTelemetry基于遥测提供程序的实现。有关如何将此提供程序设置为全局遥测提供程序的详细信息，请参阅[配置 `TelemetryProvider`](observability.md#observability-conf-telemetry-provider)。要使用此遥测提供程序，您需要以下项目资源：
+ .Instrum [OpenTelemetryentation.aws](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AWS) NuGet 软件包。
+ 遥测导出器，例如 OTLP 或控制台。有关更多信息，请参阅 OpenTelemetry文档[中的](https://opentelemetry.io/docs/languages/net/exporters/)导出器。

可以对 SDK 中包含的 OpenTelemetry 实现进行配置，以减少对 HTTPS 请求、凭证和压缩的跟踪量。为此，请将 `SuppressDownstreamInstrumentation` 选项设置为 `true`，类似于以下内容：

```
Sdk.CreateTracerProviderBuilder()
    .ConfigureResource(e => e.AddService("DemoOtel"))
    .AddAWSInstrumentation(options => options.SuppressDownstreamInstrumentation = true)
    .AddConsoleExporter()
    .Build();
```

有关此提供商的更多信息，请参阅 with [中的博客文章 “ 适用于 .NET 的 AWS SDK 增强可观察性](https://aws.amazon.com/blogs/developer/enhancing-observability-in-the-aws-sdk-for-net-with-opentelemetry/)”。 OpenTelemetry

# 其他工具
<a name="sdk-features-additional-tools"></a>

以下是一些其它工具，可用于简化 .NET 应用程序的开发、部署和维护工作。

## AWS 部署工具
<a name="sdk-features-deployment-tool"></a>

在开发计算机上开发云原生 .NET Core 应用程序后，您可以使用适用于 .NET CLI 的 AWS 部署工具更轻松地将应用程序部署到 AWS。

有关更多信息，请参阅 [将应用程序部署到 AWS](deploying.md)。

## AWS Message Processing Framework for .NET
<a name="sdk-features-msg-proc"></a>

如果您使用的是 Amazon SQS、Amazon SNS 或 Amazon EventBridge 等服务，那么您或许可以利用 AWS Message Processing Framework for .NET。有关更多信息，请参阅 [AWS Message Processing Framework for .NET](msg-proc-fw.md)。

## 与 .NET Aspire 集成
<a name="sdk-features-aspire-integrations"></a>

您可以使用与 .NET Aspire 的集成来改善内部开发循环。有关更多信息，请参阅 [在 适用于 .NET 的 AWS SDK 中将 AWS 与 .NET Aspire 集成](aspire-integrations.md)。