

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

# 使用 AWS SDK for Java 2.x
<a name="using"></a>

本章向您展示如何 AWS SDK for Java 2.x 有效地使用。了解创建服务客户端、发出请求、处理响应和管理错误。本章介绍了同步和异步编程、分页结果、资源监视 Waiter 以及性能优化。

您还将找到客户端重复使用、故障排除指导、Lambda 启动优化、HTTP/2 支持和 DNS 配置的最佳实践。

**Contents**
+ [使用提出 AWS 服务 请求 AWS SDK for Java 2.x](work-witih-clients.md)
  + [使用服务客户端发出请求](work-witih-clients.md#using-service-client)
    + [创建服务客户端](work-witih-clients.md#work-with-clients-create)
    + [默认客户端配置](work-witih-clients.md#using-default-client)
    + [配置服务客户端](work-witih-clients.md#using-configure-service-clients)
    + [关闭服务客户端](work-witih-clients.md#using-closing-client)
  + [提出请求](work-witih-clients.md#using-making-requests)
    + [使用请求来覆盖客户端配置](work-witih-clients.md#using-override-client-config-request)
  + [处理回复](work-witih-clients.md#using-handling-responses)
+ [使用AWS SDK for Java 2.x 异步编程](asynchronous.md)
  + [使用异步客户端 API](asynchronous.md#basics-async-non-streaming)
  + [使用异步方法处理流](asynchronous.md#basics-async-streaming)
  + [配置高级异步选项](asynchronous.md#advanced-operations)
+ [使用的最佳实践 AWS SDK for Java 2.x](best-practices.md)
  + [通过配置 API 超时来防止请求挂起](best-practices.md#bestpractice5)
  + [通过重复使用服务客户端来提高性能](best-practices.md#bestpractice1)
  + [通过关闭未使用的服务客户端来防止资源泄漏](best-practices.md#bestpractice-close-client)
  + [通过关闭输入流来防止连接池耗尽](best-practices.md#bestpractice2)
  + [针对您的应用程序工作负载优化 HTTP 性能](best-practices.md#bestpractice3)
  + [使用适用于异步客户端的 OpenSSL 提高 SSL 性能](best-practices.md#bestpractice4)
  + [使用 SDK 指标监控应用程序性能](best-practices.md#bestpractice6)
+ [处理中的错误 AWS SDK for Java 2.x](handling-exceptions.md)
  + [为什么使用未选中的异常？](handling-exceptions.md#why-unchecked-exceptions)
  + [AwsServiceException （和子类）](handling-exceptions.md#sdkserviceexception-and-subclasses)
  + [SdkClientException](handling-exceptions.md#sdkclientexception)
  + [异常和重试行为](handling-exceptions.md#retried-exceptions)
+ [在 2.x 中使用分页结果 适用于 Java 的 AWS SDK](pagination.md)
  + [同步分页](pagination.md#synchronous-pagination)
    + [迭代页面](pagination.md#iterate-pages)
    + [迭代对象](pagination.md#iterate-objects)
      + [使用流](pagination.md#use-a-stream)
      + [使用 for-each 循环](pagination.md#for-loop)
    + [手动分页](pagination.md#manual-pagination)
  + [异步分页](pagination.md#asynchronous-pagination)
    + [迭代表名称页面](pagination.md#iterate-pages-async)
      + [使用 `Subscriber`](pagination.md#use-a-subscriber)
      + [使用 `Consumer`](pagination.md#id1pagination)
    + [迭代表名称](pagination.md#iterate-objects-async)
      + [使用 `Subscriber`](pagination.md#id2)
      + [使用 `Consumer`](pagination.md#for-loop-async)
    + [使用第三方库](pagination.md#use-third-party-library)
+ [在中使用服务员 AWS SDK for Java 2.x](waiters.md)
  + [先决条件](waiters.md#prerequisiteswaiters)
  + [使用 Waiter](waiters.md#id1waiters)
    + [同步编程](waiters.md#synchronous-programming)
    + [异步编程](waiters.md#asynchronous-programming)
  + [配置 Waiter](waiters.md#configuring-waiters)
    + [配置 Waiter](waiters.md#configure-a-waiter)
    + [覆盖特定请求的配置](waiters.md#override-configuration-for-a-specific-request)
  + [代码示例](waiters.md#code-examples)
+ [故障排除 FAQs](troubleshooting.md)
  + [如何修复“`java.net.SocketException`: Connection reset”或“server failed to complete the response”错误？](troubleshooting.md#faq-socketexception)
  + [如何修复“连接超时”？](troubleshooting.md#faq-connection-timeout)
  + [如何修复“`java.net.SocketTimeoutException`: Read timed out”？](troubleshooting.md#faq-socket-timeout)
  + [如何修复“Unable to execute HTTP request: Timeout waiting for connection from pool”错误？](troubleshooting.md#faq-pool-timeout)
  + [我该如何修复 `NoClassDefFoundError`、`NoSuchMethodError` 或 `NoSuchFieldError`？](troubleshooting.md#faq-classpath-errors)
  + [如何修复“`SignatureDoesNotMatch`”错误或“The request signature we calculated does not match the signature you provided”错误？](troubleshooting.md#faq-signature-does-not-match-error)
  + [如何修复“`java.lang.IllegalStateException`: Connection pool shut down”错误？](troubleshooting.md#faq-connection-pool-shutdown-exception)
  + [如何修复 “无法加载来自链中任何提供商的证书 AwsCredentialsProviderChain”？](troubleshooting.md#faq-credentials-provider-chain)
    + [常见原因和解决方案](troubleshooting.md#faq-cred-provider-chain-common-causes-and-solutions)
      + [检查您的凭证配置](troubleshooting.md#faq-cred-provider-chain-check-config)
        + [对于 Amazon EC2 实例](troubleshooting.md#faq-cred-check-ec2)
        + [对于容器环境](troubleshooting.md#faq-cred-check-container-env)
        + [对于本地开发](troubleshooting.md#faq-cred-check-local-dev)
        + [对于 Web 身份联合验证](troubleshooting.md#faq-cred-check-web-id-federation)
      + [网络或代理连接问题](troubleshooting.md#faq-credentials-provider-chain-network-issues)
+ [缩短 SDK 的启动时间 AWS Lambda](lambda-optimize-starttime.md)
  + [使用 AWS 基于 CRT 的 HTTP 客户端](lambda-optimize-starttime.md#lambda-quick-url)
  + [移除未使用的 HTTP 客户端依赖项](lambda-optimize-starttime.md#lambda-quick-remove-deps)
  + [配置服务客户端以进行快捷查找](lambda-optimize-starttime.md#lambda-quick-clients)
  + [在 Lambda 函数处理程序之外初始化 SDK 客户端](lambda-optimize-starttime.md#lambda-quick-initialize)
  + [尽量减少依赖关系注入](lambda-optimize-starttime.md#lambda-quick-di)
  + [使用 Maven Archetype 瞄准 AWS Lambda](lambda-optimize-starttime.md#lambda-quick-maven)
  + [考虑一下适用于 Java 的 Lambda SnapStart](lambda-optimize-starttime.md#lambda-quick-snapstart)
  + [影响启动时间的 2.x 版更改](lambda-optimize-starttime.md#example-client-configuration)
  + [其他资源](lambda-optimize-starttime.md#lambda-quick-resources)
+ [`ContentStreamProvider`在 AWS SDK for Java 2.x](content-stream-provider.md)
  + [使用 `mark()` 和 `reset()`](content-stream-provider.md#csp-impl-mark-reset)
  + [如果 `mark()` 和 `reset()` 不可用，则使用缓冲](content-stream-provider.md#csp-impl-unsupported-streams)
  + [创建新流](content-stream-provider.md#csp-impl-new-stream)
+ [为 DNS 名称查找设置 JVM TTL](jvm-ttl-dns.md)
  + [如何设置 JVM TTL](jvm-ttl-dns.md#how-to-set-the-jvm-ttl)
    + [选项 1：在应用程序中以编程方式进行设置](jvm-ttl-dns.md#set-ttl-programmatically)
    + [选项 2：将其设置在 java.security 文件中](jvm-ttl-dns.md#set-ttl-java-security-file)
    + [选项 3：使用 JDK 系统属性回退（命令行）](jvm-ttl-dns.md#set-ttl-system-property)
+ [在 适用于 Java 的 AWS SDK 中使用 HTTP/2](http2.md)

# 使用提出 AWS 服务 请求 AWS SDK for Java 2.x
<a name="work-witih-clients"></a>

## 使用服务客户端发出请求
<a name="using-service-client"></a>

完成[设置软件开发工具包](setup.md)中的步骤并了解如何[配置服务客户端](configuring-service-clients.md)后，就可以向诸如 Amazon S3、Amazon DynamoDB AWS Identity and Access Management、Amazon EC2 等 AWS 服务提出请求了。

### 创建服务客户端
<a name="work-with-clients-create"></a>

要向发出请求 AWS 服务，必须先使用静态工厂方法为该服务实例化服务客户端。`builder()`该 `builder()` 方法返回一个允许您自定义服务客户端的 `builder` 对象。常用的 setter 方法会返回 `builder` 对象，由此可以将方法调用组合起来，这样不仅方便而且代码更加便于阅读。在配置了所需属性后，可以调用 `build()` 方法创建客户端。

例如，以下代码段将 `Ec2Client` 对象实例化为 Amazon EC2 的服务客户端。

```
Region region = Region.US_WEST_2;
Ec2Client ec2Client = Ec2Client.builder()
        .region(region)
        .build();
```

**注意**  
开发工具包中的服务客户端是线程安全的。为了获得最佳性能，应将其作为永久对象。每个客户端自己有连接池资源，当客户端收集到垃圾时相应资源会释放。  
服务客户端对象是不可变的，因此您必须为向其发出请求的每个服务创建一个新的客户端，或者，如果您希望使用不同的配置向同一服务发出请求，也需要创建一个新的客户端。  
并非所有 AWS 服务都需要`Region`在服务客户端生成器中指定；但是，最佳做法是为在应用程序中进行的 API 调用设置区域。有关更多信息，请参阅 [AWS 区域选择](region-selection.md)。

### 默认客户端配置
<a name="using-default-client"></a>

客户端生成器包含名为 `create()` 的另一个工厂方法。此方法将使用默认配置创建服务客户端。它使用[默认提供者链](credentials-chain.md)来加载凭证和[默认 AWS 区域 提供者链](region-selection.md#automatically-determine-the-aws-region-from-the-environment)。如果不能根据运行应用程序的环境确定凭证或区域，则对 `create` 的调用失败。有关 SDK 如何确定要使用的凭证和区域的更多信息，请参阅[使用凭证](credentials.md)和[区域选择](region-selection.md)。

例如，以下代码段将 `DynamoDbClient` 对象实例化为 Amazon DynamoDB 的服务客户端。

```
DynamoDbClient dynamoDbClient = DynamoDbClient.create();
```

### 配置服务客户端
<a name="using-configure-service-clients"></a>

有关如何配置服务客户端的详细信息，请参阅[通过外部方式配置客户端](configuring-service-clients-ext.md)和[代码中的客户端配置](configuring-service-clients-code.md)。

### 关闭服务客户端
<a name="using-closing-client"></a>

作为最佳实践，您应该在应用程序的生命周期内使用服务客户端进行多个 API 服务调用。但是，如果您需要一次性使用服务客户端，或者不再需要该服务客户端，请将其关闭。

当不再需要服务客户端时，请调用 `close()` 方法，以释放资源。

```
ec2Client.close();
```

如果您需要一次性使用服务客户端，则可以通过 `try`-with-resources 语句将服务客户端实例化为资源。服务客户端将实现 `[Autoclosable](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/AutoCloseable.html)` 接口，因此 JDK 会在语句末尾自动调用 `close()` 方法。

以下示例演示如何使用服务客户端进行一次性调用。调用`StsClient` AWS Security Token Service 的，将在返回账户 ID 后关闭。

```
import software.amazon.awssdk.services.sts.StsClient;

String getAccountID() {
    try (StsClient stsClient = StsClient.create()) {
       return stsClient.getCallerIdentity().account();
    }
}
```

## 提出请求
<a name="using-making-requests"></a>

使用服务客户端向对应的发出请求 AWS 服务。

例如，以下代码段演示如何创建 `RunInstancesRequest` 对象以创建新的 Amazon EC2 实例：

```
// Create the request by using the fluid setter methods of the request builder.
RunInstancesRequest runInstancesRequest = RunInstancesRequest.builder()
        .imageId(amiId)
        .instanceType(InstanceType.T1_MICRO)
        .maxCount(1)
        .minCount(1)
        .build();

// Use the configured request with the service client.
RunInstancesResponse response = ec2Client.runInstances(runInstancesRequest);
```

SDK 并不创建请求并在实例中传递，而是提供可用于创建请求的 fluent API。借助 fluent API，您可以使用 Java lambda 表达式创建请求“内联”。

以下示例使用通过[生成器创建请求的 `runInstances` 方法](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#runInstances(java.util.function.Consumer))版本重写了前面的示例。

```
// Create the request by using a lambda expression.
RunInstancesResponse response = ec2.runInstances(r -> r
                .imageId(amiId)
                .instanceType(InstanceType.T1_MICRO)
                .maxCount(1)
                .minCount(1));
```

### 使用请求来覆盖客户端配置
<a name="using-override-client-config-request"></a>

尽管服务客户端是不可变的，但您可以在请求级别覆盖它的许多设置。在构建请求时，您可以提供一个[AwsRequestOverrideConfiguration](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.html)实例来提供被覆盖的设置。可以用来覆盖客户端设置的一些方法包括：
+ `apiCallAttemptTimeout`
+ `apiCallTimeout`
+ `credentialProvider`
+ `compressionConfiguration`
+ `putHeader`

查看使用请求覆盖客户端设置的示例时，假设您有以下使用默认设置的 S3 客户端。

```
S3Client s3Client = S3Client.create();
```

您想下载一个大文件，并希望确保在下载完成之前请求不会超时。为此，请仅增大单个 `GetObject` 请求的超时值，如以下代码所示。

------
#### [ Standard API ]

```
AwsRequestOverrideConfiguration overrideConfiguration = AwsRequestOverrideConfiguration.builder()
    .apiCallTimeout(Duration.ofSeconds(100L))
    .apiCallAttemptTimeout(Duration.ofSeconds(25L))
    .build();

GetObjectRequest request = GetObjectRequest.builder()
    .bucket("amzn-s3-demo-bucket")
    .key("demo-key")
    .overrideConfiguration(overrideConfiguration)
    .build();

s3Client.getObject(request, myPath);
```

------
#### [ Fluent API ]

```
s3Client.getObject(b -> b
        .bucket("amzn-s3-demo-bucket")
        .key("demo-key")
        .overrideConfiguration(c -> c
            .apiCallTimeout(Duration.ofSeconds(100L))
            .apiCallAttemptTimeout(Duration.ofSeconds(25L))),
    myPath);
```

------

## 处理回复
<a name="using-handling-responses"></a>

对于大多数服务操作，SDK 会返回响应对象。您的代码可以根据需求处理响应对象中的信息。

例如，以下代码段输出上一个请求中随 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RunInstancesResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RunInstancesResponse.html) 对象返回的第一个实例 ID。

```
RunInstancesResponse runInstancesResponse = ec2Client.runInstances(runInstancesRequest);
System.out.println(runInstancesResponse.instances().get(0).instanceId());
```

但是，并非所有操作都会返回包含服务特定数据的响应对象。在这些情况下，您可以查询 HTTP 响应状态以了解操作是否成功。

例如，以下代码段中的代码会检查 HTTP 响应，以查看 Amazon Simple Email Service 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sesv2/SesV2Client.html#deleteContactList(software.amazon.awssdk.services.sesv2.model.DeleteContactListRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sesv2/SesV2Client.html#deleteContactList(software.amazon.awssdk.services.sesv2.model.DeleteContactListRequest)) 操作是否成功。

```
SesV2Client sesv2Client = SesV2Client.create();

DeleteContactListRequest request = DeleteContactListRequest.builder()
    .contactListName("ExampleContactListName")
    .build();

DeleteContactListResponse response = sesv2Client.deleteContactList(request);
if (response.sdkHttpResponse().isSuccessful()) {
    System.out.println("Contact list deleted successfully");
} else {
    System.out.println("Failed to delete contact list. Status code: " + response.sdkHttpResponse().statusCode());
}
```

# 使用AWS SDK for Java 2.x 异步编程
<a name="asynchronous"></a>

AWS SDK for Java 2.x 提供了异步客户端，这些客户端支持非阻塞 I/O，可以在几个线程中实现高并发。然而，并不能保证完全实现非阻塞 I/O。异步客户端在某些情况下仍可能执行阻塞调用，这些情况包括凭证检索、使用[AWS签名版本 4（SigV4）](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html)请求签名或端点发现。

同步方法会阻止执行您的线程，直到客户端接收到服务的响应。异步方法会立即返回，并控制调用的线程，而不必等待响应。

由于异步方法在收到响应之前返回，所以需要通过某种方法在响应准备就绪时接收响应。适用于 Java 的 AWS SDK 2.x 中的异步客户端方法将返回 *CompletableFuture 对象*，这些对象可让您在响应准备就绪时访问响应。

## 使用异步客户端 API
<a name="basics-async-non-streaming"></a>

异步客户端方法的签名与其对应同步方法的签名相同，但是异步方法返回 [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html) 对象，该对象*在未来*会包含异步操作的结果。如果在 SDK 的异步方法执行时发生错误，则错误会作为 `CompletionException` 引发。

您可以用来获取结果的一种方法是，将一个 `whenComplete()` 方法链接到 SDK 方法调用所返回的 `CompletableFuture`。`whenComplete()` 方法接收结果或 Throwable 类型的对象 `CompletionException`，具体取决于完成异步调用的方式。您向 `whenComplete()` 提供一个操作，用于在结果返回给调用代码之前，对结果进行处理或检查。

如果要返回的不是 SDK 方法原本返回的对象，而是其他内容，请改用 `handle()` 方法。`handle()` 方法采用的参数与 `whenComplete()` 相同，但您可以处理结果并返回对象。

要等待异步链完成并检索完成结果，可以调用 `join()` 方法。如果未在链中处理 `Throwable` 对象，则 `join()` 方法会抛出封装了原始异常的 `CompletionException`。您可以使用 `CompletionException#getCause()` 访问原始异常。您也可以调用 `CompletableFuture#get()` 方法来获取完成结果。但 `get()` 方法会抛出受检异常。

以下示例显示了两种变体，说明如何使用 DynamoDB 异步客户端的 `listTables()` 方法。传递给 `whenComplete()` 的操作仅记录成功的响应，而 `handle()` 版本则提取表名列表并返回列表。在这两种情况下，如果异步链中生成错误，则会重新抛出错误，以便客户端代码有机会对其进行处理。

 **导入**。

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;

import java.util.List;
import java.util.concurrent.CompletableFuture;
```

 **代码** 

------
#### [ whenComplete() variation ]

```
public class DynamoDbAsyncListTables {

    public static void main(String[] args) {
        Region region = Region.US_EAST_1;
        DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbAsyncClient.builder().region(region).build();
        try {
            ListTablesResponse listTablesResponse = listTablesWhenComplete(dynamoDbAsyncClient).join();  // The join() method may throw a CompletionException.
            if (listTablesResponse.hasTableNames()){
                System.out.println("Table exist in this region: " + region.id());
            }
        } catch (RuntimeException e) {
            // Handle as needed. Here we simply print out the class names.
            System.out.println(e.getClass()); // Prints 'class java.util.concurrent.CompletionException'.
            System.out.println(e.getCause().getClass()); // Prints 'class software.amazon.awssdk.services.dynamodb.model.DynamoDbException'.
        }
    }

    public static CompletableFuture<ListTablesResponse> listTablesWhenComplete(DynamoDbAsyncClient client) {
        return client.listTables(ListTablesRequest.builder().build())
            .whenComplete((listTablesResponse, throwable) -> {
                if (listTablesResponse != null) {  // Consume the response.
                    System.out.println("The SDK's listTables method completed successfully.");
                } else {
                    RuntimeException cause = (RuntimeException) throwable.getCause(); // If an error was thrown during the SDK's listTables method it is wrapped in a CompletionException.
                                                                                      // The SDK throws only RuntimeExceptions, so this is a safe cast.
                    System.out.println(cause.getMessage());  // Log error here, but rethrow so the calling code can handle as needed.
                    throw cause;
                }
            });
    }
```

------
#### [ handle() variation ]

```
public class DynamoDbAsyncListTables {

    public static void main(String[] args) {
        Region region = Region.US_EAST_1;
        DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbAsyncClient.builder().region(region).build();
        try {
            List<String> tableNames = listTablesHandle(dynamoDbAsyncClient).join(); // The join() method may throw a CompletionException.
            tableNames.forEach(System.out::println);
        } catch (RuntimeException e) {
            // Handle as needed. Here we simply print out the class names.
            System.out.println(e.getClass()); // Prints 'class java.util.concurrent.CompletionException'.
            System.out.println(e.getCause().getClass()); // Prints 'class software.amazon.awssdk.services.dynamodb.model.DynamoDbException'.
        }
    }

    public static CompletableFuture<List<String>> listTablesHandle(DynamoDbAsyncClient client) {
        return client.listTables(ListTablesRequest.builder().build())
            .handle((listTablesResponse, throwable) -> {
                if (listTablesResponse != null) {
                    return listTablesResponse.tableNames(); // Return the list of table names.
                } else {
                    RuntimeException cause = (RuntimeException) throwable.getCause(); // If an error was thrown during the SDK's listTables method it is wrapped in a CompletionException.
                                                                                      // The SDK throws only RuntimeExceptions, so this is a safe cast.
                    System.out.println(cause.getMessage());  // Log error here, but rethrow so the calling code can handle as needed.
                    throw cause;
                }
            });
    }
}
```

------

## 使用异步方法处理流
<a name="basics-async-streaming"></a>

对于包含流式内容的异步方法，必须提供 [AsyncRequestBody](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html)，以便以递增方式提供内容，或者提供 [AsyncResponseTransformer](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncResponseTransformer.html) 以接收和处理响应。

以下示例使用 `PutObject` 操作的异步形式，以异步方式将文件上传到 Amazon S3。

 **导入**。

```
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
```

 **代码** 

```
/**
 * To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class S3AsyncOps {

     public static void main(String[] args) {

         final String USAGE = "\n" +
                 "Usage:\n" +
                 "    S3AsyncOps <bucketName> <key> <path>\n\n" +
                 "Where:\n" +
                 "    bucketName - the name of the Amazon S3 bucket (for example, bucket1). \n\n" +
                 "    key - the name of the object (for example, book.pdf). \n" +
                 "    path - the local path to the file (for example, C:/AWS/book.pdf). \n" ;

        if (args.length != 3) {
            System.out.println(USAGE);
             System.exit(1);
        }

        String bucketName = args[0];
        String key = args[1];
        String path = args[2];

        Region region = Region.US_WEST_2;
        S3AsyncClient client = S3AsyncClient.builder()
                .region(region)
                .build();

        PutObjectRequest objectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        // Put the object into the bucket
        CompletableFuture<PutObjectResponse> future = client.putObject(objectRequest,
                AsyncRequestBody.fromFile(Paths.get(path))
        );
        future.whenComplete((resp, err) -> {
            try {
                if (resp != null) {
                    System.out.println("Object uploaded. Details: " + resp);
                } else {
                    // Handle error
                    err.printStackTrace();
                }
            } finally {
                // Only close the client when you are completely done with it
                client.close();
            }
        });

        future.join();
    }
}
```

以下示例使用 `GetObject` 操作的异步形式，从 Amazon S3 获取文件。

 **导入**。

```
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
```

 **代码** 

```
/**
 * To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class S3AsyncStreamOps {

    public static void main(String[] args) {

        final String USAGE = "\n" +
                "Usage:\n" +
                "    S3AsyncStreamOps <bucketName> <objectKey> <path>\n\n" +
                "Where:\n" +
                "    bucketName - the name of the Amazon S3 bucket (for example, bucket1). \n\n" +
                "    objectKey - the name of the object (for example, book.pdf). \n" +
                "    path - the local path to the file (for example, C:/AWS/book.pdf). \n" ;

        if (args.length != 3) {
            System.out.println(USAGE);
            System.exit(1);
         }

        String bucketName = args[0];
        String objectKey = args[1];
        String path = args[2];

        Region region = Region.US_WEST_2;
        S3AsyncClient client = S3AsyncClient.builder()
                .region(region)
                .build();

        GetObjectRequest objectRequest = GetObjectRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .build();

        CompletableFuture<GetObjectResponse> futureGet = client.getObject(objectRequest,
                AsyncResponseTransformer.toFile(Paths.get(path)));

        futureGet.whenComplete((resp, err) -> {
            try {
                if (resp != null) {
                    System.out.println("Object downloaded. Details: "+resp);
                } else {
                    err.printStackTrace();
                }
            } finally {
               // Only close the client when you are completely done with it
                client.close();
            }
        });
        futureGet.join();
    }
}
```

## 配置高级异步选项
<a name="advanced-operations"></a>

适用于 Java 的 AWS SDK 2.x 使用 [Netty](https://netty.io) 处理 I/O 线程，这是一种异步的事件驱动网络应用程序框架。适用于 Java 的 AWS SDK 2.x 在 Netty 之后创建 `ExecutorService`，以完成从 HTTP 客户端请求返回到 Netty 客户端的 future。这种抽象化可以减少在客服人员选择停止或休眠线程时，应用程序中断异步处理的风险。默认情况下，每个异步客户端都会根据处理器数量创建一个线程池，并管理 `ExecutorService` 队列中的任务。

在构建异步客户端时，可以指定特定的 JDK 实现 `ExecutorService`。以下代码段创建了一个具有固定线程数的 `ExecutorService`。

 **代码** 

```
S3AsyncClient clientThread = S3AsyncClient.builder()
  .asyncConfiguration(
    b -> b.advancedOption(SdkAdvancedAsyncClientOption
      .FUTURE_COMPLETION_EXECUTOR,
      Executors.newFixedThreadPool(10)
    )
  )
  .build();
```

要优化性能，您可以管理自己的线程池执行程序，并在配置客户端时包括该执行程序。

```
ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 50,
    10, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(<custom_value>),
    new ThreadFactoryBuilder()
      .threadNamePrefix("sdk-async-response").build());

// Allow idle core threads to time out
executor.allowCoreThreadTimeOut(true);

S3AsyncClient clientThread = S3AsyncClient.builder()
  .asyncConfiguration(
    b -> b.advancedOption(SdkAdvancedAsyncClientOption
      .FUTURE_COMPLETION_EXECUTOR,
      executor
    )
  )
  .build();
```

# 使用的最佳实践 AWS SDK for Java 2.x
<a name="best-practices"></a>

## 通过配置 API 超时来防止请求挂起
<a name="bestpractice5"></a>

SDK 为某些超时选项（例如连接超时和套接字超时）提供[默认值](https://github.com/aws/aws-sdk-java-v2/blob/a0c8a0af1fa572b16b5bd78f310594d642324156/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java#L134)，但不为 API 调用超时或单个 API 调用尝试超时提供默认值。为单个尝试和整个请求都设置超时是一种很好的做法。当存在可能导致请求尝试花费更长时间才能完成的临时问题或出现严重的网络问题时，这将确保您的应用程序以最佳方式快速失败。

您可以使用 `[ClientOverrideConfiguration\$1apiCallAttemptTimeout](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html#apiCallAttemptTimeout())` 和 `[ClientOverrideConfiguration\$1apiCallTimeout](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html#apiCallTimeout())` 为服务客户端发出的所有请求配置超时。

以下示例演示使用自定义超时值配置 Amazon S3 客户端。

```
S3Client.builder()
        .overrideConfiguration(
             b -> b.apiCallTimeout(Duration.ofSeconds(<custom value>))
                   .apiCallAttemptTimeout(Duration.ofMillis(<custom value>)))
        .build();
```

**`apiCallAttemptTimeout`**  
此设置设定单次 HTTP 尝试的时长，超过该时长之后可以重试 API 调用。

**`apiCallTimeout`**  
此属性的值配置整个执行的时间，包括所有重试尝试。

除了在服务客户端上设置这些超时值之外，您还可以使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/RequestOverrideConfiguration.html#apiCallTimeout()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/RequestOverrideConfiguration.html#apiCallTimeout()) 和 `[RequestOverrideConfiguration\$1apiCallAttemptTimeout()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/RequestOverrideConfiguration.html#apiCallAttemptTimeout())` 来配置单个请求。

以下示例使用自定义超时值配置单个 `listBuckets` 请求。

```
s3Client.listBuckets(lbr -> lbr.overrideConfiguration(
        b -> b.apiCallTimeout(Duration.ofSeconds(<custom value>))
               .apiCallAttemptTimeout(Duration.ofMillis(<custom value>))));
```

将这些属性一起使用时，可以对所有重试尝试所花费的总时间设置硬性限制。您还可以将单个 HTTP 请求设置为在慢速请求时快速失败。

## 通过重复使用服务客户端来提高性能
<a name="bestpractice1"></a>

每个[服务客户端](work-witih-clients.md)都维护自己的 HTTP 连接池。新请求可以重复使用池中已存在的连接，以缩短建立新连接的时间。我们建议共享单个客户端实例，以避免因未得到有效使用的连接池过多而产生的开销。所有服务客户端都是线程安全的。

如果您不想共享客户端实例，请在不需要该客户端时在示例上调用 `close()` 以释放资源。

## 通过关闭未使用的服务客户端来防止资源泄漏
<a name="bestpractice-close-client"></a>

如果不再需要某个服务客户端，请关闭该[服务客户端](work-witih-clients.md)以释放线程等资源。如果您不想共享客户端实例，请在不需要该客户端时在示例上调用 `close()` 以释放资源。

## 通过关闭输入流来防止连接池耗尽
<a name="bestpractice2"></a>

对于流式传输操作（例如 `[S3Client\$1getObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#getObject(java.util.function.Consumer,java.nio.file.Path))`），如果您直接使用 `[ResponseInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/ResponseInputStream.html)`，建议执行以下操作：
+ 尽快读取输入流中的所有数据。
+ 请尽快关闭输入流。

我们之所以提出这些建议，是因为输入流是来自 HTTP 连接的直接数据流，并且在读取流中的所有数据并关闭流之前，底层 HTTP 连接无法重复使用。如果不遵守这些规则，则客户端可能会因为分配太多已打开但未使用的 HTTP 连接而耗尽资源。

## 针对您的应用程序工作负载优化 HTTP 性能
<a name="bestpractice3"></a>

SDK 提供了一组适用于一般用例的[默认 http 配置](https://github.com/aws/aws-sdk-java-v2/blob/master/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java)。我们建议客户根据其用例调整其应用程序的 HTTP 配置。

作为一个很好的起点，SDK 提供了[智能配置默认值](http-configuration.md#http-config-smart-defaults)功能。此功能从版本 2.17.102 开始提供。根据您的用例选择一种模式，该模式将提供合理的配置值。

## 使用适用于异步客户端的 OpenSSL 提高 SSL 性能
<a name="bestpractice4"></a>

默认情况下，SDK 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html) 使用 JDK 的默认 SSL 实现作为 `SslProvider`。我们的测试发现，OpenSSL 的性能比 JDK 的默认实现要好。Netty 社区也[建议使用 OpenSSL](https://netty.io/wiki/requirements-for-4.x.html#tls-with-openssl)。

要使用 OpenSSL，请将 `netty-tcnative` 添加到您的依赖项中。有关配置的详细信息，请参阅 [Netty 项目文档](https://netty.io/wiki/forked-tomcat-native.html)。

在为项目配置了 `netty-tcnative` 后，`NettyNioAsyncHttpClient` 实例将自动选择 OpenSSL。或者，您可以使用 `NettyNioAsyncHttpClient` 生成器显式设置 `SslProvider`，如以下代码段所示。

```
NettyNioAsyncHttpClient.builder()
                        .sslProvider(SslProvider.OPENSSL)
                        .build();
```

## 使用 SDK 指标监控应用程序性能
<a name="bestpractice6"></a>

适用于 Java 的 SDK 可以[收集应用程序中服务客户端的指标](metrics.md)。您可以使用这些指标来识别性能问题、查看总体使用趋势、查看返回的服务客户端异常或深入了解特定问题。

我们建议您收集指标，然后分析 Amazon CloudWatch 日志，以便更深入地了解应用程序的性能。

# 处理中的错误 AWS SDK for Java 2.x
<a name="handling-exceptions"></a>

了解 AWS SDK for Java 2.x 抛出异常的方式和时间对于使用 SDK 构建高质量的应用程序非常重要。接下来几节介绍开发工具包引发异常的几种不同情况，以及如何正确地处理这些异常。

## 为什么使用未选中的异常？
<a name="why-unchecked-exceptions"></a>

出于以下原因， 适用于 Java 的 AWS SDK 使用运行时（或未选中）异常而不是已检查的异常：
+ 使开发人员能够精细控制要处理哪些错误，而不是必须处理无关紧要的异常情况（这会导致代码极其冗长）
+ 避免大型应用程序因使用选中的异常而固有的可扩展性问题

一般来说，小型应用程序使用选中的异常是可以的，但随着应用程序的大小和复杂程度增加，这样做就会出现问题。

## AwsServiceException （和子类）
<a name="sdkserviceexception-and-subclasses"></a>

 [AwsServiceException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html)是您在使用时遇到的最常见的异常 适用于 Java 的 AWS SDK。 `AwsServiceException`是更通[SdkServiceException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html)用的子类。 `AwsServiceException`s 表示来自 a 的错误响应 AWS 服务。例如，如果您尝试终止一个不存在的 Amazon EC2 实例，则 Amazon EC2 将返回错误响应，并且该错误响应的所有详细信息都将包含在抛出的错误响应中。`AwsServiceException`

遇到时`AwsServiceException`，您就知道您的请求已成功发送到， AWS 服务 但无法成功处理。这可能是因为请求的参数中存在错误，或者是因为服务端的问题。

 `AwsServiceException` 为您提供很多信息，例如：
+ 返回的 HTTP 状态代码
+ 返回的 AWS 错误码
+ [AwsErrorDetails](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsErrorDetails.html)课堂上来自服务的详细错误消息
+  AWS 失败请求的请求 ID

在某些情况下，会引发 `AwsServiceException` 的一个特定于服务的子类，使开发人员能够通过捕获模块精细控制如何处理错误情况。的 Java SDK API 参考中[AwsServiceException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html)显示了大量的`AwsServiceException`子类。使用子类链接可深入查看服务引发的细粒度异常。

例如，以下指向 SDK API 参考的链接中提供了一些常用 AWS 服务的异常层次结构。每个页面上的子类列表显示了您的代码可以捕获的特定异常。
+ [Amazon S3](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/S3Exception.html)
+ [DynamoDB](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)
+ [Amazon SQS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SqsException.html)

要了解有关异常的更多信息，请检查[AwsErrorDetails](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsErrorDetails.html)对象`errorCode`上的。您可以使用该 `errorCode` 值在服务指南 API 中查找信息。例如，如果捕获到 `S3Exception` 且 `AwsErrorDetails#errorCode()` 的值为 `InvalidRequest`，则使用《Amazon S3 API Reference》中的 [List of error codes](https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList) 来查看更多详细信息。

## SdkClientException
<a name="sdkclientexception"></a>

 [SdkClientException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkClientException.html)表示 Java 客户端代码内部出现问题，无论是在尝试向发送请求时 AWS 还是尝试解析来自 AWS的响应时。`SdkClientException`通常比 a 更严重`SdkServiceException`，表示存在阻止客户端向 AWS 服务发出服务调用的主要问题。例如，当您尝试在其中一个客户端上调用操作时，`SdkClientException`如果没有可用的网络连接，则会 适用于 Java 的 AWS SDK 抛出。

## 异常和重试行为
<a name="retried-exceptions"></a>

适用于 Java 的 SDK 会重试多个[客户端异常](https://github.com/aws/aws-sdk-java-v2/blob/13985e0668a9a0b12ad331644e3c4fd1385c2cd7/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/retry/SdkDefaultRetrySetting.java#L79C41-L79C41)请求以及从响应中 AWS 服务 收到的 [HTTP 状态代码](https://github.com/aws/aws-sdk-java-v2/blob/13985e0668a9a0b12ad331644e3c4fd1385c2cd7/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/retry/SdkDefaultRetrySetting.java#L72C31-L72C31)的请求。这些错误作为服务客户端默认使用的旧版 `RetryMode` 的一部分进行处理。有关 `[RetryMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryMode.html)` 的 Java API 参考描述了可用于配置模式的各种方式。

要自定义触发自动重试的异常和 HTTP 状态代码，请使用添加 `[RetryOnExceptionsCondition](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryOnExceptionsCondition.html)` 和 `[RetryOnStatusCodeCondition](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/retry/conditions/RetryOnErrorCodeCondition.html)` 实例的 `[RetryPolicy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html)` 来配置服务客户端。

# 在 2.x 中使用分页结果 适用于 Java 的 AWS SDK
<a name="pagination"></a>

当响应对象太大而无法在单个响应中返回时，许多 AWS 操作都会返回分页结果。在 适用于 Java 的 AWS SDK 1.0 中，响应包含一个用于检索下一页结果的标记。相比之下， 适用于 Java 的 AWS SDK 2.x 具有自动分页方法，可以进行多次服务调用，自动为您获取下一页的结果。您只需编写处理结果的代码。自动分页功能适用于同步和异步客户端。

**注意**  
这些代码段假设您了解[使用 SDK 的基础知识](using.md)，并且已为环境配置了[单点登录访问权限](get-started-auth.md#setup-credentials)。

## 同步分页
<a name="synchronous-pagination"></a>

以下示例演示列出 Amazon S3 桶中对象的同步分页方法。

### 迭代页面
<a name="iterate-pages"></a>

第一个示例演示如何使用 `listRes` 分页工具对象以及 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/paginators/ListObjectsV2Iterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/paginators/ListObjectsV2Iterable.html) 实例，借助 `stream` 方法迭代所有响应页面。代码在响应页面上进行流式传输，将响应流转换为`[S3Object](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/S3Object.html)`内容流，然后处理 Amazon S3 对象的内容。

以下导入适用于此同步分页部分中的所有示例。

#### 导入
<a name="synchronous-pagination-ex-import"></a>

```
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.DeleteBucketRequest;
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse;
import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.s3.model.CompletedPart;
import software.amazon.awssdk.services.s3.model.CreateBucketConfiguration;
import software.amazon.awssdk.services.s3.model.UploadPartRequest;
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
```

```
        ListObjectsV2Request listReq = ListObjectsV2Request.builder()
            .bucket(bucketName)
            .maxKeys(1)
            .build();

        ListObjectsV2Iterable listRes = s3.listObjectsV2Paginator(listReq);
        // Process response pages
        listRes.stream()
            .flatMap(r -> r.contents().stream())
            .forEach(content -> System.out
                .println(" Key: " + content.key() + " size = " + content.size()));
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L112) GitHub。

### 迭代对象
<a name="iterate-objects"></a>

以下示例演示了迭代响应中返回的对象（而不是响应的页面）的方法。`ListObjectsV2Iterable` 类的 `contents` 方法返回一个 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html)，它提供了几种处理底层内容元素的方法。

#### 使用流
<a name="use-a-stream"></a>

以下代码段在响应内容上使用 `stream` 方法来迭代分页项目集合。

```
        // Helper method to work with paginated collection of items directly.
        listRes.contents().stream()
            .forEach(content -> System.out
                .println(" Key: " + content.key() + " size = " + content.size()));
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L127) GitHub。

#### 使用 for-each 循环
<a name="for-loop"></a>

由于 `SdkIterable` 扩展了 `Iterable` 接口，因此您可以像处理任何 `Iterable` 一样处理内容。以下代码段使用标准 `for-each` 循环迭代响应的内容。

```
        for (S3Object content : listRes.contents()) {
            System.out.println(" Key: " + content.key() + " size = " + content.size());
        }
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L133) GitHub。

### 手动分页
<a name="manual-pagination"></a>

如果您的使用案例需要手动分页，则手动分页仍然可用。对后续请求使用响应对象中的下一个令牌。以下示例使用 `while` 循环。

```
        ListObjectsV2Request listObjectsReqManual = ListObjectsV2Request.builder()
            .bucket(bucketName)
            .maxKeys(1)
            .build();

        boolean done = false;
        while (!done) {
            ListObjectsV2Response listObjResponse = s3.listObjectsV2(listObjectsReqManual);
            for (S3Object content : listObjResponse.contents()) {
                System.out.println(content.key());
            }

            if (listObjResponse.nextContinuationToken() == null) {
                done = true;
            }

            listObjectsReqManual = listObjectsReqManual.toBuilder()
                .continuationToken(listObjResponse.nextContinuationToken())
                .build();
        }
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L90) GitHub。

## 异步分页
<a name="asynchronous-pagination"></a>

以下示例演示了列出 DynamoDB 表格的异步分页方法。

### 迭代表名称页面
<a name="iterate-pages-async"></a>

以下两个示例使用异步 DynamoDB 客户端，该客户端调用 `listTablesPaginator` 方法并请求获取 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/paginators/ListTablesPublisher.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/paginators/ListTablesPublisher.html)。`ListTablesPublisher` 实现了两个接口，这为处理响应提供了许多选项。我们将研究每个接口的方法。

#### 使用 `Subscriber`
<a name="use-a-subscriber"></a>

以下代码示例演示如何使用 `ListTablesPublisher` 实现的 `org.reactivestreams.Publisher` 接口处理分页结果。要了解有关响应式流模型的更多信息，请参阅 React [ive St GitHub reams 存储库](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.2/README.md)。

以下导入适用于此异步分页部分中的所有示例。

##### 导入
<a name="use-a-subscriber-ex-imports"></a>

```
import io.reactivex.rxjava3.core.Flowable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.publisher.Flux;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
import software.amazon.awssdk.services.dynamodb.paginators.ListTablesPublisher;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
```

以下代码获取一个 `ListTablesPublisher` 实例。

```
        // Creates a default client with credentials and region loaded from the
        // environment.
        final DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.create();

        ListTablesRequest listTablesRequest = ListTablesRequest.builder().limit(3).build();
        ListTablesPublisher publisher = asyncClient.listTablesPaginator(listTablesRequest);
```

以下代码使用 `org.reactivestreams.Subscriber` 的匿名实现来处理每个页面的结果。

`onSubscribe` 方法将调用 `Subscription.request` 方法来对来自发布者的数据启动请求。必须调用此方法以开始从发布者获取数据。

订阅者的 `onNext` 方法将处理响应页面，它会访问所有表名称并打印出每个表名称。处理完该页面后，会向发布者请求另一个页面。将重复调用该方法，直到检索了所有页面。

如果检索数据时出现错误，将触发 `onError` 方法。最后，在 `onComplete` 方法在请求所有页面后调用。

```
        // A Subscription represents a one-to-one life-cycle of a Subscriber subscribing
        // to a Publisher.
        publisher.subscribe(new Subscriber<ListTablesResponse>() {
            // Maintain a reference to the subscription object, which is required to request
            // data from the publisher.
            private Subscription subscription;

            @Override
            public void onSubscribe(Subscription s) {
                subscription = s;
                // Request method should be called to demand data. Here we request a single
                // page.
                subscription.request(1);
            }

            @Override
            public void onNext(ListTablesResponse response) {
                response.tableNames().forEach(System.out::println);
                // After you process the current page, call the request method to signal that
                // you are ready for next page.
                subscription.request(1);
            }

            @Override
            public void onError(Throwable t) {
                // Called when an error has occurred while processing the requests.
            }

            @Override
            public void onComplete() {
                // This indicates all the results are delivered and there are no more pages
                // left.
            }
        });
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L83) GitHub。

#### 使用 `Consumer`
<a name="id1pagination"></a>

`ListTablesPublisher` 实现的 `SdkPublisher` 接口有一个 `subscribe` 方法，该方法接受 `Consumer` 并返回 `CompletableFuture<Void>`。

此接口中的 `subscribe` 方法可用于 `org.reactivestreams.Subscriber` 开销可能过大的简单用例。当下面的代码使用每个页面时，它会在每个页面上调用 `tableNames` 方法。该 `tableNames` 方法返回使用 `forEach` 方法处理的 DynamoDB 表名的 `java.util.List`。

```
        // Use a Consumer for simple use cases.
        CompletableFuture<Void> future = publisher.subscribe(
                response -> response.tableNames()
                        .forEach(System.out::println));
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L96) GitHub。

### 迭代表名称
<a name="iterate-objects-async"></a>

以下示例演示了迭代响应中返回的对象（而不是响应的页面）的方法。与之前用 `contents` 方法演示的同步 Amazon S3 示例类似，DynamoDB 异步结果类 `ListTablesPublisher` 具有与底层项目集合交互的 `tableNames` 便捷方法。此 `tableNames` 方法的返回类型是一个 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html)，可用于跨所有页面请求项目。

#### 使用 `Subscriber`
<a name="id2"></a>

以下代码获取表名底层集合的 `SdkPublisher`。

```
        // Create a default client with credentials and region loaded from the
        // environment.
        final DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.create();

        ListTablesRequest listTablesRequest = ListTablesRequest.builder().limit(3).build();
        ListTablesPublisher listTablesPublisher = asyncClient.listTablesPaginator(listTablesRequest);
        SdkPublisher<String> publisher = listTablesPublisher.tableNames();
```

以下代码使用 `org.reactivestreams.Subscriber` 的匿名实现来处理每个页面的结果。

订阅者的 `onNext` 方法将处理集合中的单个元素。在本例中，它是一个表名称。处理完该表名称后，会向发布者请求另一个表名称。将重复调用该方法，直到检索了所有表名称。

```
        // Use a Subscriber.
        publisher.subscribe(new Subscriber<String>() {
            private Subscription subscription;

            @Override
            public void onSubscribe(Subscription s) {
                subscription = s;
                subscription.request(1);
            }

            @Override
            public void onNext(String tableName) {
                System.out.println(tableName);
                subscription.request(1);
            }

            @Override
            public void onError(Throwable t) {
            }

            @Override
            public void onComplete() {
            }
        });
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L147) GitHub。

#### 使用 `Consumer`
<a name="for-loop-async"></a>

以下示例使用 `SdkPublisher` 的 `subscribe` 方法（采用 `Consumer`）来处理每个项目。

```
        // Use a Consumer.
        CompletableFuture<Void> future = publisher.subscribe(System.out::println);
        future.get();
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L161) GitHub。

### 使用第三方库
<a name="use-third-party-library"></a>

您可以使用其他第三方库，而不是实现自定义订阅者。此示例演示了的用法 RxJava，但是可以使用任何实现响应式流接口的库。有关该库的更多信息[， GitHub请参阅上的 RxJava wiki 页面](https://github.com/ReactiveX/RxJava/wiki)。

要使用该库，请将其作为依赖项添加。如果使用了 Maven，示例将显示要使用的 POM 代码段。

 **POM 条目** 

```
<dependency>
      <groupId>io.reactivex.rxjava3</groupId>
      <artifactId>rxjava</artifactId>
      <version>3.1.6</version>
</dependency>
```

 **代码** 

```
        DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.create();
        ListTablesPublisher publisher = asyncClient.listTablesPaginator(ListTablesRequest.builder()
                .build());

        // The Flowable class has many helper methods that work with
        // an implementation of an org.reactivestreams.Publisher.
        List<String> tables = Flowable.fromPublisher(publisher)
                .flatMapIterable(ListTablesResponse::tableNames)
                .toList()
                .blockingGet();
        System.out.println(tables);
```

请参阅上的[完整示例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L198) GitHub。

# 在中使用服务员 AWS SDK for Java 2.x
<a name="waiters"></a>

 适用于 Java 的 AWS SDK 2.x 的 waiters 实用程序使您能够在对这些 AWS 资源执行操作之前验证这些资源是否处于指定状态。

*服务员*是一种抽象概念，用于轮询 AWS 资源，例如 DynamoDB 表或 Amazon S3 存储桶，直到达到所需的状态（或者直到确定资源永远无法达到所需状态）。与其编写逻辑来持续轮询 AWS 资源（这可能很繁琐且容易出错），不如使用服务员来轮询资源，让您的代码在资源准备就绪后继续运行。

## 先决条件
<a name="prerequisiteswaiters"></a>

必须先完成[设置 适用于 Java 的 AWS SDK 2.](setup.md) x 中的步骤 适用于 Java 的 AWS SDK，然后才能在项目中使用服务员。

您还必须将项目依赖项（例如，在您的 `pom.xml` 或 `build.gradle` 文件中）配置为使用 适用于 Java 的 AWS SDK版本 `2.15.0` 或更高版本。

例如：

```
<project>
  <dependencyManagement>
   <dependencies>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>bom</artifactId>
        <version>2.27.21</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
   </dependencies>
  </dependencyManagement>
</project>
```

## 使用 Waiter
<a name="id1waiters"></a>

要实例化 waiter 对象，请先创建一个服务客户端。将服务客户端的 `waiter()` 方法设置为 waiter 对象的值。waiter 实例存在后，设置其响应选项以执行相应的代码。

### 同步编程
<a name="synchronous-programming"></a>

以下代码片段显示了如何等待 DynamoDB 表存在并处于 ACTIVE **状态**。

```
DynamoDbClient dynamo = DynamoDbClient.create();
DynamoDbWaiter waiter = dynamo.waiter();

WaiterResponse<DescribeTableResponse> waiterResponse =
  waiter.waitUntilTableExists(r -> r.tableName("myTable"));

// print out the matched response with a tableStatus of ACTIVE
waiterResponse.matched().response().ifPresent(System.out::println);
```

### 异步编程
<a name="asynchronous-programming"></a>

以下代码片段显示了如何等待 DynamoDB 表不再存在。

```
DynamoDbAsyncClient asyncDynamo = DynamoDbAsyncClient.create();
DynamoDbAsyncWaiter asyncWaiter = asyncDynamo.waiter();

CompletableFuture<WaiterResponse<DescribeTableResponse>> waiterResponse =
          asyncWaiter.waitUntilTableNotExists(r -> r.tableName("myTable"));

waiterResponse.whenComplete((r, t) -> {
  if (t == null) {
   // print out the matched ResourceNotFoundException
   r.matched().exception().ifPresent(System.out::println);
  }
}).join();
```

## 配置 Waiter
<a name="configuring-waiters"></a>

您可以使用 waiter 的生成器上的 `overrideConfiguration()`，为 waiter 自定义配置。对于某些操作，您可以在发出请求时应用自定义配置。

### 配置 Waiter
<a name="configure-a-waiter"></a>

以下代码段演示如何覆盖 waiter 的配置。

```
// sync
DynamoDbWaiter waiter =
   DynamoDbWaiter.builder()
          .overrideConfiguration(b -> b.maxAttempts(10))
          .client(dynamoDbClient)
          .build();
// async
DynamoDbAsyncWaiter asyncWaiter =
   DynamoDbAsyncWaiter.builder()
          .client(dynamoDbAsyncClient)
          .overrideConfiguration(o -> o.backoffStrategy(
               FixedDelayBackoffStrategy.create(Duration.ofSeconds(2))))
          .scheduledExecutorService(Executors.newScheduledThreadPool(3))
          .build();
```

### 覆盖特定请求的配置
<a name="override-configuration-for-a-specific-request"></a>

以下代码段演示如何根据每个请求覆盖 waiter 的配置。请注意，只有某些操作具有可自定义的配置。

```
waiter.waitUntilTableNotExists(b -> b.tableName("myTable"),
               o -> o.maxAttempts(10));

asyncWaiter.waitUntilTableExists(b -> b.tableName("myTable"),
                 o -> o.waitTimeout(Duration.ofMinutes(1)));
```

## 代码示例
<a name="code-examples"></a>

有关将 waiters 与一起使用的完整示例 DynamoDB，请参阅 AWS 代码示例[CreateTable存储库中的.java。](https://github.com/awsdocs/aws-doc-sdk-examples/blob/869b7ddbc7c8f66c7c45acd5b813429aff37003e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTable.java)

有关使用服务员的完整示例 Amazon S3，请参阅 AWS 代码示例[存储库中的 S3 BucketOps .java。](https://github.com/awsdocs/aws-doc-sdk-examples/blob/869b7ddbc7c8f66c7c45acd5b813429aff37003e/javav2/example_code/s3/src/main/java/com/example/s3/S3BucketOps.java)

# 故障排除 FAQs
<a name="troubleshooting"></a>

在应用程序 AWS SDK for Java 2.x 中使用时，可能会遇到本主题中列出的运行时错误。使用此处的建议来帮助找出根本原因并解决错误。

## 如何修复“`java.net.SocketException`: Connection reset”或“server failed to complete the response”错误？
<a name="faq-socketexception"></a>

连接重置错误表示您的主机 AWS 服务、或任何中间方（例如，NAT 网关、代理、负载均衡器）在请求完成之前关闭了连接。由于原因有很多，因此要找到解决方案，就必须知道连接关闭的原因。以下各项通常会导致连接关闭。
+ **连接处于非活动状态。**这种情况在流式操作中很常见：由于一段时间内没有任何数据通过网络进行读写，中间方检测到连接已经失效并将其关闭。为防止出现这种情况，请确保您的应用程序会主动下载或上传数据。
+ **您关闭了 HTTP 或 SDK 客户端。**确保不要在资源仍在使用时将其关闭。
+ **代理配置错误。**尝试绕过您配置的任何代理，以查看这样是否可以解决问题。如果这样可以解决问题，则表明代理出于某种原因正在关闭您的连接。请研究您的特定代理以确定代理关闭连接的原因。

如果您无法识别问题，请尝试在网络的客户端边缘（例如，在您控制的任何代理之后）为受影响的连接运行 TCP 转储。

如果您看到 AWS 端点正在发送`TCP RST`（重置），[请联系受影响的服务](https://aws.amazon.com/contact-us/)，看看他们能否确定重置的原因。请准备好提供问题发生的请求 IDs 和时间戳。 AWS 支持团队还可能受益于[电汇日志，这些日志](logging-slf4j.md#sdk-java-logging-verbose)可以准确显示您的应用程序发送和接收的字节以及何时发送。

## 如何修复“连接超时”？
<a name="faq-connection-timeout"></a>

连接超时错误表示您的主机 AWS 服务、或任何中间方（例如，NAT 网关、代理、负载均衡器）未能在配置的连接超时内与服务器建立新连接。以下项目列出了导致此问题的常见原因。
+ **配置的连接超时太低。**默认情况下， AWS SDK for Java 2.x中的连接超时为 2 秒。如果将连接超时设置得太低，则可能会出现此错误。如果您只进行区域内调用，则建议的连接超时为 1 秒；如果您发出跨区域请求，则建议的连接超时为 3 秒。
+ **代理配置错误。**尝试绕过您配置的任何代理，以查看这样是否可以解决问题。如果这样可以解决问题，则表明连接超时的根源在于代理。研究您的特定代理以确定发生这种情况的原因

如果您无法识别问题，请尝试在网络的客户端边缘（例如，在您控制的任何代理之后）为受影响的连接运行 TCP 转储，以便调查是否存在任何网络问题。

## 如何修复“`java.net.SocketTimeoutException`: Read timed out”？
<a name="faq-socket-timeout"></a>

读取超时错误表示 JVM 尝试从底层操作系统读取数据，但未在通过 SDK 配置的时间内返回数据。如果操作系统、或任何中间方（例如 AWS 服务，NAT 网关、代理、负载均衡器）未能在 JVM 预期的时间内发送数据，则可能会发生此错误。由于原因有很多，因此要找到解决方案，就必须知道为什么没有返回数据。

尝试在网络的客户端边缘（例如，在您控制的任何代理之后）为受影响的连接运行 TCP 转储。

如果您看到 AWS 终端节点正在发送`TCP RST`（重置），[请联系受影响的服务](https://aws.amazon.com/contact-us/)。请准备好提供问题发生的请求 IDs 和时间戳。 AWS 支持团队还可能受益于[电汇日志，这些日志](logging-slf4j.md#sdk-java-logging-verbose)可以准确显示您的应用程序发送和接收的字节以及何时发送。

## 如何修复“Unable to execute HTTP request: Timeout waiting for connection from pool”错误？
<a name="faq-pool-timeout"></a>

此错误表示请求无法在指定的最长时间内从池中获得连接。要解决问题，我们建议您[启用 SDK 客户端指标](metrics.md)，以便向 Amazon CloudWatch 发布指标。HTTP 指标有助于缩小根本原因范围。以下项目列出了导致此错误的常见原因。
+ **连接泄露。**您可以通过检查 `LeasedConcurrency`、`AvailableConcurrency` 和 `MaxConcurrency` 指标来调查此问题。如果 `LeasedConcurrency` 持续增长，直至达到 `MaxConcurrency` 但从未下降，则可能存在连接泄露。发生泄露的常见原因是流式操作（例如 S3 `getObject` 方法）未关闭。我们建议您的应用程序尽快读取输入流中的所有数据，[之后关闭输入流](best-practices.md#bestpractice2)。下图显示了连接泄露的 SDK 指标看起来可能是什么样子。  
![\[显示可能存在连接泄漏的 CloudWatch 指标的屏幕截图。\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/images/JavaDevGuide-connection-leak-metrics-chart.png)
+ **连接池匮乏。**如果您的请求速率过高，并且已配置的连接池大小无法满足请求需求，则可能会发生这种情况。默认连接池大小为 50，当池中的连接达到最大值时，HTTP 客户端会将传入的请求排入队列，直到连接可用为止。下图显示了连接池匮乏的 SDK 指标看起来可能是什么样子。  
![\[显示连接池饥饿情况的 CloudWatch 指标屏幕截图。\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/images/JavaDevGuide-connection-pool-starvation-chart.png)

  要缓解此问题，请考虑采取以下任何措施。
  + 增加连接池的大小。
  + 增加获取超时。
  + 降低请求速率。

  通过增加最大连接数，可以增加客户端吞吐量（除非网络接口已充分利用）。但是，您最终可能会达到操作系统对进程所使用的文件描述符数量施加的限制。如果您已经充分使用网络接口或无法进一步增加连接数，请尝试增大获取超时时间。随着时间的增加，在超时之前，您可以获得更多时间来请求获取连接。如果连接没有释放，则后续请求仍将超时。

  如果您无法通过前两种机制解决问题，请尝试通过以下选项来降低请求速率。
  + 将您的请求流量平滑化，避免突发的大流量冲击导致客户端过载。
  + 通过呼叫提高效率 AWS 服务。
  + 增加发送请求的主机数量。
+ **I/O 线程太忙。**这仅适用于使用基于 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html) 的异步 SDK 客户端的情况。如果该`AvailableConcurrency`指标不低（表示池中存在连接），而是`ConcurrencyAcquireDuration`很高，则可能是因为 I/O 线程无法处理请求。确保你没有以[未来完成执行者的`Runnable:run`](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/SdkAdvancedAsyncClientOption.html#FUTURE_COMPLETION_EXECUTOR)身份通过，也不要在响应的未来完成链中执行耗时的任务，因为这可能会阻塞 I/O 线程。如果不是这样，请考虑使用该`[eventLoopGroupBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.Builder.html#eventLoopGroupBuilder(software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup.Builder))`方法增加 I/O 线程数。作为参考，`NettyNioAsyncHttpClient` 实例的默认 I/O 线程数是主机 CPU 核心数的两倍。
+ **TLS 握手延迟高。**如果您的 `AvailableConcurrency` 指标接近 0 且 `LeasedConcurrency` 小于 `MaxConcurrency`，则可能是因为 TLS 握手延迟很高。下图显示了 TLS 握手延迟高的 SDK 指标可能是什么样子。  
![\[可能表明 TLS 握手延迟较高的 CloudWatch 指标的屏幕截图。\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/images/JavaDevGuide-high-tls-latency-chart.png)

  对于 Java SDK 提供的未基于 CRT 的 HTTP 客户端，请尝试启用 [TLS 日志](security-java-tls.md)来解决 TLS 问题。对于基于 AWS CRT 的 HTTP 客户端，请尝试启用 [AWS CRT 日志](logging-slf4j.md#sdk-java-logging-verbose)。如果您发现 AWS 端点似乎需要很长时间才能执行 TLS 握手，则应[联系受影响的服务](https://aws.amazon.com/contact-us/)。

## 我该如何修复 `NoClassDefFoundError`、`NoSuchMethodError` 或 `NoSuchFieldError`？
<a name="faq-classpath-errors"></a>

`NoClassDefFoundError` 表示在运行时无法加载某个类。出现此错误的两个最常见原因是：
+ 因为 JAR 丢失或类路径上的 JAR 版本不正确，该类在类路径中不存在。
+ 因为该类的静态初始化器引发了异常，所以该类无法加载。

同样，`NoSuchMethodError` 和 `NoSuchFieldError` 通常是由于 JAR 版本不匹配所致。我们建议您执行以下步骤。

1. **检查您的依赖项**，确保使用的*所有 SDK JAR 包版本一致*。找不到类、方法或字段的最常见原因是，您升级到新的客户端版本，但继续使用旧的“共享”SDK 依赖项版本。新的客户端版本可能会尝试使用仅存在于较新的“共享”SDK 依赖项中的类。尝试运行 `mvn dependency:tree` 或 `gradle dependencies`（对于 Gradle）来验证 SDK 库版本是否全部匹配。为了完全避免将来出现此问题，我们建议使用 [BOM（物料清单）](setup-project-maven.md#sdk-as-dependency)来管理 SDK 模块版本。

   下面的示例演示混合使用 SDK 版本的示例。

   ```
   [INFO] +- software.amazon.awssdk:dynamodb:jar:2.20.00:compile
   [INFO] |  +- software.amazon.awssdk:aws-core:jar:2.13.19:compile
   [INFO] +- software.amazon.awssdk:netty-nio-client:jar:2.20.00:compile
   ```

   `dynamodb` 的版本是 2.20.00，`aws-core` 的版本是 2.13.19。`aws-core` 构件版本也应为 2.20.00。

1. **在日志的早期部分检查相关语句**，以查看某个类是否因静态初始化失败而无法加载。当类第一次加载失败时，可能会抛出一个不同的、更有用的异常来指定无法加载该类的*原因*。这个可能有用的异常只发生一次，因此后续日志语句只会报告未找到该类。

1. **检查您的部署过程**，确保所需的 JAR 文件已随应用程序一同部署。您可能确实使用了正确的版本进行构建，但为应用程序创建类路径的过程却排除了一个必需的依赖项。

## 如何修复“`SignatureDoesNotMatch`”错误或“The request signature we calculated does not match the signature you provided”错误？
<a name="faq-signature-does-not-match-error"></a>

`SignatureDoesNotMatch`错误表示生成的签名 适用于 Java 的 AWS SDK 和生成的签名 AWS 服务 不匹配。以下项目说明了潜在原因。
+ 代理或中间方修改请求。例如，代理或负载均衡器可能会修改由 SDK 签名的标头、路径或查询字符串。
+ 服务和 SDK 在各自生成待签名字符串时，对请求进行编码的方式不一致。

要调试此问题，我们建议您为 SDK [启用调试日志记录](logging-slf4j.md#sdk-debug-level-logging)。尝试重现错误并找到 SDK 生成的规范请求。在日志中，规范请求用 `AWS4 Canonical Request: ...` 标记，待签名字符串用 `AWS4 String to sign: ...` 标记。

如果您无法启用调试（例如，因为错误只能在生产环境中重现），请向应用程序添加逻辑，以便在错误发生时记录有关请求的信息。然后，您可以使用该信息，尝试在生产环境之外（例如，在启用了调试日志记录的集成测试中）复现该错误。

收集规范请求和待签名字符串后，将其与 [AWS 签名版本 4 规范](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html)进行比较，以确定 SDK 生成待签名字符串的方式是否存在问题。如果出现问题，可以创建[GitHub 错误报告](https://github.com/aws/aws-sdk-java-v2/issues/new/choose)给 适用于 Java 的 AWS SDK。

如果没有出现任何问题，您可以将 SDK 的待签字符串与失败响应中 AWS 服务 返回的待签字符串（例如 Amazon S3）进行比较。如果未提供此信息，则应[联系受影响服务的支持人员](https://aws.amazon.com/contact-us/)，以查看他们生成了哪些规范请求和待签名字符串，以便进行比较。这些比较有助于识别是否有中间方可能修改了请求，或发现服务与客户端之间在编码处理上的差异。

有关签署请求的更多背景信息，请参阅 AWS Identity and Access Management 用户指南中的[签署 AWS API 请求](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html)。

**Example 规范请求的**  

```
PUT
/Example-Bucket/Example-Object
partNumber=19&uploadId=string
amz-sdk-invocation-id:f8c2799d-367c-f024-e8fa-6ad6d0a1afb9
amz-sdk-request:attempt=1; max=4
content-encoding:aws-chunked
content-length:51
content-type:application/octet-stream
host:xxxxx
x-amz-content-sha256:STREAMING-UNSIGNED-PAYLOAD-TRAILER
x-amz-date:20240308T034733Z
x-amz-decoded-content-length:10
x-amz-sdk-checksum-algorithm:CRC32
x-amz-trailer:x-amz-checksum-crc32
```

**Example 待签名字符串的**  

```
AWS4-HMAC-SHA256
20240308T034435Z
20240308/us-east-1/s3/aws4_request
5f20a7604b1ef65dd89c333fd66736fdef9578d11a4f5d22d289597c387dc713
```

## 如何修复“`java.lang.IllegalStateException`: Connection pool shut down”错误？
<a name="faq-connection-pool-shutdown-exception"></a>

此错误表示底层 Apache HTTP 连接池已关闭。以下项目说明了潜在原因。
+ **SDK 客户端过早关闭。**只有在关闭了关联客户端时，SDK 才会关闭连接池。确保不要在资源仍在使用时将其关闭。
+ **出现 `java.lang.Error` 错误。**`OutOfMemoryError` 等错误会导致 Apache HTTP 连接池[关闭](https://github.com/apache/httpcomponents-client/blob/6a741b4f8f23e6c5c7cc42c36c2acabfac19c3d6/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java#L368)。检查您的日志，找出错误的堆栈跟踪信息。还要检查您的代码中是否存在捕获了 `Throwable` 或 `Error` 但却“吞掉”输出，从而导致错误无法暴露出来的地方。如果您的代码未报告错误，请重写代码，以便可以记录信息。记录的信息有助于确定错误的根本原因。
+ **在 `DefaultCredentialsProvider#create()` 返回的凭证提供程序关闭后您仍然尝试使用它**。[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html#create()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html#create()) 返回一个单例实例，因此，如果它已关闭且您的代码调用了 `resolveCredentials` 方法，则在缓存的凭证（或令牌）过期后会引发异常。

  检查您的代码中是否存在关闭了 `DefaultCredentialsProvider` 的地方，如下面的示例中所示。
  + 通过调用 `DefaultCredentialsProvider#close().` 来关闭单例实例。

    ```
    DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create(); // Singleton instance returned.
    AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials();
    
    // Make calls to AWS 服务.
    
    defaultCredentialsProvider.close();  // Explicit close.
    
    // Make calls to AWS 服务.
    
    // After the credentials expire, either of the following calls eventually results in a "Connection pool shut down" exception.
    credentials = defaultCredentialsProvider.resolveCredentials();
    // Or
    credentials = DefaultCredentialsProvider.create().resolveCredentials();
    ```
  + 在 try-with-resources方块`DefaultCredentialsProvider#create()`中调用。

    ```
    try (DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create()) {
        AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials();
        
        // Make calls to AWS 服务.
    
    } // After the try-with-resources block exits, the singleton DefaultCredentialsProvider is closed.
    
    // Make calls to AWS 服务.
    
    DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create(); // The closed singleton instance is returned.
    // If the credentials (or token) has expired, the following call results in the error.
    AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials();
    ```

  如果您的代码已关闭单例实例，并且您需要使用 `DefaultCredentialsProvider` 解析凭证，则通过调用 `DefaultCredentialsProvider.builder().build()` 来创建新的非单例实例。

## 如何修复 “无法加载来自链中任何提供商的证书 AwsCredentialsProviderChain”？
<a name="faq-credentials-provider-chain"></a>

此错误表示 AWS SDK for Java 2.x 无法通过默认 AWS 凭证提供者链中的任何证书提供者找到有效的凭证。SDK 会自动按特定顺序搜索凭证，当链中的所有提供程序都无法提供有效凭证时，就会发生此错误。

完整的错误消息通常如下所示（为了提高可读性，添加了行尾结束符号和缩进）：

```
Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain(
    credentialsProviders=[
        SystemPropertyCredentialsProvider(),
        EnvironmentVariableCredentialsProvider(), 
        WebIdentityTokenCredentialsProvider(), 
        ProfileCredentialsProvider(profileName=default, profileFile=ProfileFile(sections=[])), 
        ContainerCredentialsProvider(), 
        InstanceProfileCredentialsProvider()
    ]) : [
        SystemPropertyCredentialsProvider(): Unable to load credentials from system settings.
        Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) 
        or system property (aws.accessKeyId)., 

        EnvironmentVariableCredentialsProvider(): Unable to load credentials from system settings. 
        Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) 
        or system property (aws.accessKeyId)., 

        WebIdentityTokenCredentialsProvider(): To use web identity tokens, the 'sts' service module 
        must be on the class path., 

        ProfileCredentialsProvider(profileName=default, profileFile=ProfileFile(sections=[])): 
        Profile file contained no credentials for profile 'default': ProfileFile(sections=[]), 

        ContainerCredentialsProvider(): Cannot fetch credentials from container - neither 
        AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment 
        variables are set., 

        InstanceProfileCredentialsProvider(): Failed to load credentials from IMDS.]
```

### 常见原因和解决方案
<a name="faq-cred-provider-chain-common-causes-and-solutions"></a>

#### 检查您的凭证配置
<a name="faq-cred-provider-chain-check-config"></a>

当您使用默认凭证提供程序（在没有显式配置凭证的情况下调用 `ServiceClient.create()`）时，SDK 会按特定顺序搜索凭证。查看[默认凭证提供程序链的工作原理](credentials-chain.md)，以了解 SDK 会检查哪些凭证来源以及按什么顺序检查。

确保在您的环境中正确设置了您打算使用的凭证配置方法：

##### 对于 Amazon EC2 实例
<a name="faq-cred-check-ec2"></a>
+ **检查 IAM 角色：**验证 IAM 角色是否已附加到您的实例。
+ **IMDS 间歇性故障：**如果您遇到间歇性故障（通常持续几百毫秒），这通常表示实例元数据服务（IMDS）出现暂时性网络问题。

  解决方案：
  + 启用[调试日志记录](logging-slf4j.md#sdk-debug-level-logging)以分析发生故障的时间和频率
  + 考虑在应用程序中实施重试逻辑以应对与凭证相关的故障
  + 检查您的实例和 IMDS 端点之间的网络连接问题

##### 对于容器环境
<a name="faq-cred-check-container-env"></a>

确认已配置任务角色（Amazon ECS）或服务账户（Amazon EKS），并已设置所需的环境变量。

##### 对于本地开发
<a name="faq-cred-check-local-dev"></a>

确认已设置好环境变量、凭证文件或 IAM Identity Center 配置。

##### 对于 Web 身份联合验证
<a name="faq-cred-check-web-id-federation"></a>
+ **验证配置：**确认存在 Web 身份令牌文件且已配置所需的环境变量。
+ **缺少 STS 模块依赖项：**如果您看到错误 `To use web identity tokens, the 'sts' service module must be on the class path`，则需要将 STS 模块添加为依赖项。在使用 Amazon EKS 容器组身份或其他 Web 身份令牌身份验证时，这种情况很常见。

  解决方案：将 STS 模块添加到您的项目依赖项中：
  + 

    ```
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>sts</artifactId>
    </dependency>
    ```

    对于某些服务，您可能还需要 `aws-query-protocol` 依赖项：

    ```
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>aws-query-protocol</artifactId>
    </dependency>
    ```

#### 网络或代理连接问题
<a name="faq-credentials-provider-chain-network-issues"></a>

如果您在凭证提供程序链中看到 `Connection refused` 错误，这通常表示 SDK 尝试访问 AWS 端点时存在网络连接问题。

**解决方案**
+ 如果正在使用代理服务器，请验证代理配置
+ 检查您的网络是否允许到 AWS 终端节点的出站 HTTPS 连接
+ 启用[调试日志记录](logging-slf4j.md#sdk-debug-level-logging)以查看详细的连接尝试
+ 使用 `curl` 等工具测试连接，以便验证 AWS 端点的网络访问

# 缩短 SDK 的启动时间 AWS Lambda
<a name="lambda-optimize-starttime"></a>

的目标之一 AWS SDK for Java 2.x 是减少 AWS Lambda 函数的启动延迟。SDK 包含可缩短启动时间的更改，本主题末尾将对此进行讨论。

首先，本主题重点介绍为缩短冷启动时间可以进行的更改。这包括更改代码结构和服务客户端的配置。

## 使用 AWS 基于 CRT 的 HTTP 客户端
<a name="lambda-quick-url"></a>

对于使用 AWS Lambda，我们建议在同步场景中使用，[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtHttpClient.html)对于异步场景，我们建议使用。

本指南中的 [配置 AWS 基于 CRT 的 HTTP 客户](http-configuration-crt.md) 主题描述了使用 HTTP 客户端的好处、如何添加依赖项以及如何按服务客户端配置其使用。

## 移除未使用的 HTTP 客户端依赖项
<a name="lambda-quick-remove-deps"></a>

除了明确使用 AWS 基于 CRT 的客户端外，您还可以移除 SDK 默认引入的其他 HTTP 客户端。当需要加载的库较少时，Lambda 启动时间会缩短，因此您应该移除 JVM 需要加载的所有未使用的构件。

以下 Maven `pom.xml` 文件片段展示了排除基于 Apache 的 HTTP 客户端和基于 Netty 的 HTTP 客户端的情况。（当您使用 AWS 基于 CRT 的客户端时，不需要这些客户端。） 此示例将 HTTP 客户端项目排除在 S3 客户端依赖项之外，并添加了该`aws-crt-client`对象以允许访问 AWS 基于 CRT 的 HTTP 客户端。

```
<project>
    <properties>
        <aws.java.sdk.version>2.27.21</aws.java.sdk.version>
    <properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>${aws.java.sdk.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>aws-crt-client</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>software.amazon.awssdk</groupId>
                    <artifactId>netty-nio-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>software.amazon.awssdk</groupId>
                    <artifactId>apache-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>
```

**注意**  
将 `<exclusions>` 元素添加到 `pom.xml` 文件中的所有服务客户端依赖项中。

## 配置服务客户端以进行快捷查找
<a name="lambda-quick-clients"></a>

**指定区域**  
创建服务客户端时，在服务客户端生成器上调用 `region` 方法。这简化了 SDK 的默认[区域查找过程，该过程](region-selection.md#default-region-provider-chain)会在多个位置检查 AWS 区域 信息。  
要使 Lambda 代码独立于区域，请在 `region` 方法中使用以下代码。此代码访问 Lambda 容器设置的 `AWS_REGION` 环境变量。  

```
Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))
```

**使用 `EnvironmentVariableCredentialProvider`**  
与区域信息的默认查找行为非常相似，SDK 会在多个位置查找凭证。通过在生成服务客户端时指定 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.html)，可以节省 SDK 凭证查找过程的时间。  
使用此凭证提供程序可以将代码用于 Lambda 函数，但可能无法在 Amazon EC2 或其他系统上运行。  
如果您打算在某个时候使用 [Lambd SnapStart a for Java](#lambda-quick-snapstart)，则应依靠默认的凭证提供程序链来查找证书。如果您指定`EnvironmentVariableCredentialsProvider`，则初始凭证查找起作用，但是激活后 SnapStart ，[Java 运行时会设置容器凭据环境变量](https://docs.aws.amazon.com/lambda/latest/dg/snapstart-activate.html#snapstart-credentials)。激活后，`EnvironmentVariableCredentialsProvider` 使用的环境变量（访问密钥环境变量）对 Java SDK 不可用。

以下代码段显示了为在 Lambda 环境中使用而经过适当配置的 S3 服务客户端。

```
S3Client s3Client = S3Client.builder()
    .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
    .credentialsProvider(EnvironmentVariableCredentialsProvider.create())
    .httpClient(AwsCrtHttpClient.builder().build())
    .build();
```

## 在 Lambda 函数处理程序之外初始化 SDK 客户端
<a name="lambda-quick-initialize"></a>

我们建议在 Lambda 处理程序方法之外初始化 SDK 客户端。这样，如果重复使用执行上下文，则可以跳过服务客户端的初始化。通过重复使用客户端实例及其连接，处理程序方法的后续调用可更快进行。

在以下示例中，使用静态工厂方法在构造函数中初始化 `S3Client` 实例。如果重复使用由 Lambda 环境管理的容器，则会重复使用初始化的 `S3Client` 实例。

```
public class App implements RequestHandler<Object, Object> {
    private final S3Client s3Client;

    public App() {
        s3Client = DependencyFactory.s3Client();
    }

    @Override
    public Object handle Request(final Object input, final Context context) {
         ListBucketResponse response = s3Client.listBuckets();
         // Process the response.
    }
}
```

## 尽量减少依赖关系注入
<a name="lambda-quick-di"></a>

依赖关系注入 (DI) 框架可能需要更多时间才能完成设置过程。它们可能还需要额外的依赖项，这需要一段时间才能加载。

如果需要 DI 框架，建议使用诸如 [Dagger](https://dagger.dev/dev-guide/) 之类的轻量级 DI 框架。

## 使用 Maven Archetype 瞄准 AWS Lambda
<a name="lambda-quick-maven"></a>

 AWS Java SDK 团队开发了一个 [Maven Archetype](https://github.com/aws/aws-sdk-java-v2/tree/master/archetypes/archetype-lambda) 模板，可以在最短的启动时间内启动 Lambda 项目。您可以从该原型构建 Maven 项目，并知道依赖项的配置非常适合 Lambda 环境。

要了解有关原型的更多信息并完成示例部署，请参阅此[博客文章](https://aws.amazon.com/blogs/developer/bootstrapping-a-java-lambda-application-with-minimal-aws-java-sdk-startup-time-using-maven/)。

## 考虑一下适用于 Java 的 Lambda SnapStart
<a name="lambda-quick-snapstart"></a>

如果您的运行时要求兼容，则 AWS 提供适用[ SnapStart 于 Java 的 Lambda](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html)。Lambda SnapStart 是一种基于基础设施的解决方案，可提高 Java 函数的启动性能。当您发布新版本的函数时，Lambda 会对其进行 SnapStart 初始化，并拍摄内存和磁盘状态的不可变加密快照。 SnapStart 然后缓存快照以供重复使用。

## 影响启动时间的 2.x 版更改
<a name="example-client-configuration"></a>

除了您对代码所做的更改外，适用于 Java 的 SDK 2.x 版本还包括三项可缩短启动时间的主要更改：
+ 使用 [jackson-jr](https://github.com/FasterXML/jackson-jr)，它是一个序列化库，可以改进初始化时间
+ 对日期和时间对象使用 [java.time](https://docs.oracle.com/javase/8/docs/api/index.html?java/time.html) 库，此为 JDK 的一部分。
+ 对记录 facade 使用 [Slf4j](https://www.slf4j.org/)。

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

 AWS Lambda 开发者指南中有一[节介绍开发非 Java 特定的 Lambda 函数的最佳实践](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html)。

有关使用 Java 构建云原生应用程序的示例 AWS Lambda，请参阅此[研讨会内容](https://github.com/aws-samples/aws-lambda-java-workshop)。该研讨会讨论了性能优化和其他最佳实践。

您可以考虑使用提前编译的静态映像来减少启动延迟。例如，您可以使用适用于 Java 的 SDK 2.x 和 Maven 来[构建 GraalVM 原生映像](setup-project-graalvm.md)。

# `ContentStreamProvider`在 AWS SDK for Java 2.x
<a name="content-stream-provider"></a>

`ContentStreamProvider`是中使用的抽象， AWS SDK for Java 2.x 用于允许多次读取输入数据。本主题将介绍如何为应用程序正确实施 `ContentStreamProvider`。

适用于 Java 的 SDK 2.x 每次需要读取整个流时使用 `ContentStreamProvider#newStream()` 方法。要让此方法适用于整个流，返回的流必须始终位于内容的起始位置，并且必须包含相同的数据。

在接下来的章节中，我们将提供三种正确实现该行为的方法。

## 使用 `mark()` 和 `reset()`
<a name="csp-impl-mark-reset"></a>

在下面的示例中，我们在读取开始之前在构造函数中使用 `mark(int)`，以确保我们可以将流重置回开头。对于 `newStream()` 的每次调用，我们都会重置流：

```
public class MyContentStreamProvider implements ContentStreamProvider {  
    private InputStream contentStream;  
  
    public MyContentStreamProvider(InputStream contentStream) {  
        this.contentStream = contentStream;  
        this.contentStream.mark(MAX_LEN);  
    }  
  
    @Override  
    public InputStream newStream() {  
        contentStream.reset();  
        return contentStream;  
    }  
}
```

## 如果 `mark()` 和 `reset()` 不可用，则使用缓冲
<a name="csp-impl-unsupported-streams"></a>

 如果您的流不直接支持 `mark()` 和 `reset()`，您仍然可以使用前面显示的解决方案，方法是先将流封装在 `BufferedInputStream` 中：

```
public class MyContentStreamProvider implements ContentStreamProvider {  
    private BufferedReader contentStream;  
  
    public MyContentStreamProvider(InputStream contentStream) {  
        this.contentStream = new BufferedInputStream(contentStream);  
        this.contentStream.mark(MAX_LEN);
    }  
  
    @Override  
    public InputStream newStream() {  
        contentStream.reset();  
        return contentStream;  
    }  
}
```

## 创建新流
<a name="csp-impl-new-stream"></a>

一种更简单的方法是：每次调用时都直接获取一个全新的数据流，并关闭前一个数据流：

```
public class MyContentStreamProvider implements ContentStreamProvider {  
    private InputStream contentStream;  
  
    @Override  
    public InputStream newStream() {  
        if (contentStream != null) {  
            contentStream.close();  
        }  
        contentStream = openStream();  
        return contentStream;  
    }  
}
```

# 为 DNS 名称查找设置 JVM TTL
<a name="jvm-ttl-dns"></a>

Java 虚拟机 (JVM) 缓存 DNS 名称查找。当 JVM 将主机名解析为 IP 地址时，它会将该 IP 地址缓存一段指定的时间，即 *time-to-live*(TTL)。

由于 AWS 资源使用的 DNS 名称条目偶尔会发生变化，因此我们建议您将 JVM 的 TTL 值配置为 5 秒。这可确保在资源的 IP 地址发生更改时，您的应用程序将能够通过重新查询 DNS 来接收和使用资源的新 IP 地址。

对于一些 Java 配置，将设置 JVM 默认 TTL，以便在重新启动 JVM 之前*绝不* 刷新 DNS 条目。因此，如果在应用程序仍在运行时 AWS 资源的 IP 地址发生变化，则在您*手动重启* JVM 并刷新缓存的 IP 信息之前，它将无法使用该资源。在此情况下，设置 JVM 的 TTL，以便定期刷新其缓存的 IP 信息是极为重要的。

## 如何设置 JVM TTL
<a name="how-to-set-the-jvm-ttl"></a>

要修改 JVM 的 TTL，请设置 net [workaddress.cache.ttl 安全属性值。](https://docs.oracle.com/en/java/javase/17/core/java-networking.html#GUID-A680DADB-C4C1-40F1-B568-D9A97C917F5D)请注意，`networkaddress.cache.ttl`这是一个*安全属性*，而不是系统属性，也就是说，它不能使用`-D`命令行标志进行设置。

### 选项 1：在应用程序中以编程方式进行设置
<a name="set-ttl-programmatically"></a>

在应用程序启动的[https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/Security.html](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/Security.html)早期、创建任何 AWS SDK 客户端之前和发出任何网络请求之前调用：

```
import java.security.Security;

public class MyApplication {
    public static void main(String[] args) {
        Security.setProperty("networkaddress.cache.ttl", "5");

        // ... create SDK clients and run application
    }
}
```

### 选项 2：将其设置在 java.security 文件中
<a name="set-ttl-java-security-file"></a>

在`$JAVA_HOME/jre/lib/security/java.security`文件中为 Java 8 或更高版本设置该`networkaddress.cache.ttl`属性，在 Java 11 或更高版本`$JAVA_HOME/conf/security/java.security`的文件中设置该属性。

以下是 `java.security` 文件中的代码段，该代码段显示 TTL 缓存设置为 5 秒。

```
#
# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#
...
networkaddress.cache.ttl=5
...
```

在由`$JAVA_HOME`环境变量表示的 JVM 上运行的所有应用程序都使用此设置。

### 选项 3：使用 JDK 系统属性回退（命令行）
<a name="set-ttl-system-property"></a>

如果无法修改安全配置或代码，则可以使用 JDK 系统属性。如果未定义安全属性，则这些属性充当后备选项。
+ `sun.net.inetaddr.ttl`— 控制成功查找（TTL 为正）
+ `sun.net.inetaddr.negative.ttl`— 控制失败的查找（负的 TTL）

```
java -Dsun.net.inetaddr.ttl=5 -Dsun.net.inetaddr.negative.ttl=1 -jar myapp.jar
```

**注意**  
这些是 [Oracle Java 8 网络属性参考文献中记录的 JDK 内部属性](https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html)，作为 “未来的版本可能不支持” 的私有属性。尽可能使用选项 1-2。

# 在 适用于 Java 的 AWS SDK 中使用 HTTP/2
<a name="http2"></a>

HTTP/2 是 HTTP 协议的一个主要修订。这一新版本具有多个增强功能以提高性能：
+ 二进制数据编码提供了更高效的数据传输。
+ 标头压缩可减少客户端下载的开销字节数，同时帮助客户端更快地获取内容。这对于受带宽限制的移动客户端尤其有用。
+ 双向异步通信（多路复用）可让客户端和 AWS 之间的多个请求和响应消息同时通过单一连接（而不是通过多个连接）进行传输，这样可以提高性能。

升级到最新开发工具包的开发人员将自动使用 HTTP/2（如果他们使用的服务支持）。新编程接口无缝地利用 HTTP/2 功能并提供新的方法来构建应用程序。

适用于 Java 的 AWS SDK 2.x 提供了新的实施 HTTP/2 协议的 API 进行事件流式传输。有关如何使用这些新 API 的示例，请参阅[使用 Kinesis](examples-kinesis.md)。