

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

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

本章說明如何 AWS SDK for Java 2.x 有效使用 。了解如何建立服務用戶端、提出請求、處理回應和管理錯誤。本章涵蓋同步和非同步程式設計、分頁結果、資源監控的等待者，以及效能最佳化。

您也可以找到用戶端重複使用、疑難排解指引、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)
  + [使用非同步用戶端 APIs](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)
+ [在 適用於 Java 的 AWS SDK 2.x 中使用分頁結果](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)
  + [使用等待程式](waiters.md#id1waiters)
    + [同步程式設計](waiters.md#synchronous-programming)
    + [非同步程式設計](waiters.md#asynchronous-programming)
  + [設定等待程式](waiters.md#configuring-waiters)
    + [設定等待程式](waiters.md#configure-a-waiter)
    + [覆寫特定請求的組態](waiters.md#override-configuration-for-a-specific-request)
  + [程式碼範例](waiters.md#code-examples)
+ [疑難排解常見問答集](troubleshooting.md)
  + [如何修正「`java.net.SocketException`：連線重設」或「伺服器無法完成回應」錯誤？](troubleshooting.md#faq-socketexception)
  + [如何修正「連線逾時」？](troubleshooting.md#faq-connection-timeout)
  + [如何修正「`java.net.SocketTimeoutException`：讀取逾時」？](troubleshooting.md#faq-socket-timeout)
  + [如何修正「無法執行 HTTP 請求：等待從集區連線逾時」錯誤？](troubleshooting.md#faq-pool-timeout)
  + [如何修正 `NoClassDefFoundError`、 `NoSuchMethodError`或 `NoSuchFieldError`？](troubleshooting.md#faq-classpath-errors)
  + [如何修正「`SignatureDoesNotMatch`」錯誤或「我們計算的請求簽章與您提供的簽章不符」錯誤？](troubleshooting.md#faq-signature-does-not-match-error)
  + [如何修正「`java.lang.IllegalStateException`：連線集區關閉」錯誤？](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 原型目標 AWS Lambda](lambda-optimize-starttime.md#lambda-quick-maven)
  + [考慮 Lambda SnapStart for Java](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)
+ [在 中使用 HTTP/2 適用於 Java 的 AWS SDK](http2.md)

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

## 使用服務用戶端提出請求
<a name="using-service-client"></a>

完成[設定 SDK](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`物件。Fluent 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`式中的資源。服務用戶端實作 `[Autoclosable](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/AutoCloseable.html)`界面，因此 JDK 會在 陳述式結尾自動呼叫 `close()`方法。

下列範例示範如何使用服務用戶端進行一次性呼叫。呼叫 `StsClient`的 會在傳回帳戶 ID 後 AWS Security Token Service 關閉。

```
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);
```

開發套件提供流暢的 API，可讓您用來建立請求，而不是建立請求並在執行個體中傳遞。透過流暢的 API，您可以使用 Java lambda 表達式來建立請求「內嵌」。

下列範例會使用使用建置器建立請求的 `runInstances`方法版本，來重寫先前的範例。 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#runInstances(java.util.function.Consumer)](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 Signature 第 4 版 (SigV4)](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) 簽署請求，或端點探索。

同步方法會封鎖您的執行緒執行，直到用戶端收到服務的回應。非同步方法會立即傳回，將控制權回歸給呼叫端執行緒，無需等待回應。

由於非同步方法會在有可用回應之前傳回，您需要一個方法在回應準備好時取得回應。 適用於 Java 的 AWS SDK 傳回 *CompletableFuture 物件*的 2.x 中非同步用戶端的方法，可讓您在回應準備就緒時存取回應。

## 使用非同步用戶端 APIs
<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()` 方法`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;
```

 **Code** 

------
#### [ 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) 以接收和處理回應。

下列範例使用 Amazon S3 非同步形式的 `PutObject`操作，以非同步方式將檔案上傳至 。

 **匯入** 

```
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;
```

 **Code** 

```
/**
 * 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;
```

 **Code** 

```
/**
 * 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 用戶端傳回的未來。如果開發人員選擇停止或睡眠執行緒，則此抽象概念可降低應用程式中斷非同步程序的風險。根據預設，每個非同步用戶端都會根據處理器數量建立執行緒集區，並管理 內佇列中的任務`ExecutorService`。

您可以在建置非同步用戶端`ExecutorService`時指定 的特定 JDK 實作。下列程式碼片段會建立`ExecutorService`具有固定執行緒數目的 。

 **Code** 

```
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>

開發套件提供一些逾時選項的[預設值](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 組態。

開發套件提供[智慧型組態預設值](http-configuration.md#http-config-smart-defaults)功能，是很好的起點。此功能從 2.17.102 版開始可用。您可以根據您的使用案例選擇模式，以提供合理的組態值。

## 使用非同步用戶端的 OpenSSL 改善 SSL 效能
<a name="bestpractice4"></a>

根據預設，開發套件的 [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 Logs，以便更深入了解應用程式的效能。

# 處理 中的錯誤 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`代表來自 的錯誤回應 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`會擲回服務特定的 子類別，以允許開發人員精細控制透過擷取區塊處理錯誤案例。適用於 [AwsServiceException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html) 的 Java SDK API 參考會顯示大量`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 參考中的[錯誤代碼清單](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` 通常比 更嚴重`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`服務用戶端預設使用之舊版的一部分來處理。的 Java API 參考`[RetryMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryMode.html)`說明您可以設定 模式的各種方式。

若要自訂觸發自動重試的例外狀況和 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)`的 來設定您的服務用戶端。

# 在 適用於 Java 的 AWS SDK 2.x 中使用分頁結果
<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()));
```

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

### 反覆運算物件
<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()));
```

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

#### 使用 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());
        }
```

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

### 手動分頁
<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();
        }
```

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

## 非同步分頁
<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>

下列程式碼範例示範如何使用 實作的`org.reactivestreams.Publisher`界面來處理分頁結果`ListTablesPublisher`。若要進一步了解被動串流模型，請參閱[被動串流 GitHub 儲存庫](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.
            }
        });
```

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

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

`ListTablesPublisher` 實作 的`SdkPublisher`界面具有採用 `Consumer`並傳回 `subscribe`的方法`CompletableFuture<Void>`。

來自此界面的 `subscribe`方法可用於簡單的使用案例，當 `org.reactivestreams.Subscriber`可能過多的額外負荷時。由於以下程式碼會耗用每個頁面，因此會呼叫每個頁面上的 `tableNames`方法。`tableNames` 方法會傳回使用 `forEach`方法處理的 `java.util.List` DynamoDB 資料表名稱的 。

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

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

### 反覆運算資料表名稱
<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() {
            }
        });
```

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

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

下列範例使用 的 `subscribe`方法`SdkPublisher`，需要 `Consumer`來處理每個項目。

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

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

### 使用第三方程式庫
<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>
```

 **Code** 

```
        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);
```

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

# 在 中使用等待程式 AWS SDK for Java 2.x
<a name="waiters"></a>

 適用於 Java 的 AWS SDK 2.x 的等待程式公用程式可讓您在對 AWS 這些資源執行操作之前，先驗證資源是否處於指定的狀態。

*等待程式*是用來輪詢 AWS 資源的抽象，例如 DynamoDB 資料表或 Amazon S3 儲存貯體，直到達到所需的狀態 （或直到確定資源永遠不會達到所需的狀態）。您可以使用等待程式輪詢資源，並讓程式碼在資源準備就緒後繼續執行，而不是編寫邏輯來持續輪詢 AWS 可能很繁瑣且容易出錯的資源。

## 先決條件
<a name="prerequisiteswaiters"></a>

您必須先完成[設定 適用於 Java 的 AWS SDK 2.x](setup.md) 中的步驟 適用於 Java 的 AWS SDK，才能在專案中使用等待程式。

您也必須將專案相依性 （例如，在您的 `pom.xml`或 `build.gradle` 檔案中） 設定為使用 版本 `2.15.0` 或更新版本 適用於 Java 的 AWS SDK。

例如：

```
<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>
```

## 使用等待程式
<a name="id1waiters"></a>

若要執行個體化等待程式物件，請先建立服務用戶端。將服務用戶端的 `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();
```

## 設定等待程式
<a name="configuring-waiters"></a>

您可以使用其建置器`overrideConfiguration()`上的 來自訂等待程式的組態。對於某些操作，您可以在提出請求時套用自訂組態。

### 設定等待程式
<a name="configure-a-waiter"></a>

下列程式碼片段顯示如何覆寫等待程式上的組態。

```
// 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.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>

如需搭配 使用等待程式的完整範例 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 程式碼範例儲存庫中的 [S3BucketOps.java](https://github.com/awsdocs/aws-doc-sdk-examples/blob/869b7ddbc7c8f66c7c45acd5b813429aff37003e/javav2/example_code/s3/src/main/java/com/example/s3/S3BucketOps.java)。

# 疑難排解常見問答集
<a name="troubleshooting"></a>

當您 AWS SDK for Java 2.x 在應用程式中使用 時，您可能會遇到本主題中列出的執行時間錯誤。使用這裡的建議來協助您找出根本原因並解決錯誤。

## 如何修正「`java.net.SocketException`：連線重設」或「伺服器無法完成回應」錯誤？
<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 閘道、代理、負載平衡器） 無法在設定的連線逾時內與伺服器建立新的連線。以下項目說明此問題的常見原因。
+ **設定的連線逾時太低。**根據預設， 中的連線逾時為 2 秒 AWS SDK for Java 2.x。如果您設定太低的連線逾時，您可能會收到此錯誤。如果您只進行區域內呼叫，則建議的連線逾時為 1 秒；如果您提出跨區域請求，則建議的連線逾時為 3 秒。
+ **設定錯誤的代理。**嘗試略過您設定的任何代理，以查看它是否修正了問題。如果這修正了問題，代理就是連線逾時的原因。研究您的特定代理，以判斷發生這種情況的原因

如果您無法識別問題，請嘗試在網路的用戶端邊緣 （例如，在您控制的任何代理之後） 為受影響的連線執行 TCP 傾印，以調查任何網路問題。

## 如何修正「`java.net.SocketTimeoutException`：讀取逾時」？
<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)，該日誌確切顯示應用程式傳送和接收的位元組，以及傳送和接收時間。

## 如何修正「無法執行 HTTP 請求：等待從集區連線逾時」錯誤？
<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_tw/sdk-for-java/latest/developer-guide/images/JavaDevGuide-connection-leak-metrics-chart.png)
+ **連線集區匱乏。 **如果您的請求率太高，且已設定的連線集區大小無法滿足請求需求，就會發生這種情況。預設連線集區大小為 50，當集區中的連線達到上限時，HTTP 用戶端會將傳入請求排入佇列，直到連線可用為止。下圖顯示連線集區匱乏的 SDK 指標可能看起來像什麼。  
![\[CloudWatch 指標的螢幕擷取畫面，顯示連線集區匱乏的外觀。\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/images/JavaDevGuide-connection-pool-starvation-chart.png)

  若要緩解此問題，請考慮採取下列任何動作。
  + 增加連線集區大小，
  + 增加取得逾時。
  + 減慢請求率。

  透過增加連線數量上限，用戶端輸送量可以增加 （除非網路界面已充分利用）。不過，您最終可以對程序使用的檔案描述項數量達到操作系統限制。如果您已經完全使用網路界面，或無法進一步增加連線計數，請嘗試增加取得逾時。隨著增加，您可以在逾時之前獲得請求取得連線的額外時間。如果連線未釋出，後續請求仍會逾時。

  如果您無法使用前兩個機制修正問題，請嘗試以下選項來降低請求率。
  + 平滑您的請求，讓大型流量爆增不會讓用戶端超載。
  + 呼叫 時更有效率 AWS 服務。
  + 增加傳送請求的主機數量。
+ **I/O 執行緒太忙碌。**這僅適用於使用非同步 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)。如果`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 交握延遲很高。下表顯示 SDK 指標在高 TLS 交握延遲時可能看起來像什麼。  
![\[可能表示高 TLS 交握延遲的 CloudWatch 指標螢幕擷取畫面。\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/images/JavaDevGuide-high-tls-latency-chart.png)

  對於不是以 CRT 為基礎的 Java 開發套件提供的 HTTP 用戶端，請嘗試啟用 [TLS 日誌](security-java-tls.md)來疑難排解 TLS 問題。對於以 CRT 為基礎的 HTTP AWS 用戶端，請嘗試啟用 [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` 表示無法在執行時間載入類別。此錯誤的兩個最常見原因是：
+ 類別不存在於 classpath 中，因為 JAR 遺失或 JAR 的錯誤版本位於 classpath 上。
+ 類別無法載入，因為其靜態初始化器擲回例外狀況。

同樣地， `NoSuchMethodError`和 `NoSuchFieldError`通常由不相符的 JAR 版本造成。我們建議您執行下列步驟。

1. **檢查您的相依性**，以確保您使用*所有 SDK jar 的相同版本*。類別、方法或欄位找不到的最常見原因是當您升級至新的用戶端版本，但您繼續使用舊的「共用」開發套件相依版本時。新的用戶端版本可能會嘗試使用僅存在於較新「共用」 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 檔案。您可能會使用正確的版本建置 ，但為您的應用程式建立 classpath 的程序會排除必要的相依性。

## 如何修正「`SignatureDoesNotMatch`」錯誤或「我們計算的請求簽章與您提供的簽章不符」錯誤？
<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 Signature 第 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`：連線集區關閉」錯誤？
<a name="faq-connection-pool-shutdown-exception"></a>

此錯誤表示基礎 Apache HTTP 連線集區已關閉。以下項目說明潛在原因。
+ **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.builder().build()` 如果您的程式碼已關閉單一執行個體，而且您需要使用 解析登入資料，請呼叫 來建立新的非單一執行個體`DefaultCredentialsProvider`。

## 如何修正「無法從鏈結 AwsCredentialsProviderChain 中的任何供應商載入登入資料」？
<a name="faq-credentials-provider-chain"></a>

此錯誤表示 AWS SDK for Java 2.x 無法透過預設 AWS 登入資料提供者鏈結中的任何登入資料提供者找到有效的登入資料。軟體開發套件會自動以特定順序搜尋登入資料，當鏈結中的所有供應商無法提供有效的登入資料時，就會發生此錯誤。

完整的錯誤訊息通常如下所示 （新增行尾和縮排以提高可讀性）：

```
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()`而不明確設定登入資料） 時，軟體開發套件會依特定順序搜尋登入資料。檢閱[預設登入資料提供者鏈結的運作方式](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 Pod Identity 或其他 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 端點時的網路連線問題。

**解決方案：**
+ 如果您使用代理伺服器，請驗證代理組態
+ 檢查您的網路是否允許對外 HTTPS 連線至 AWS 端點
+ 啟用[偵錯記錄](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 啟動延遲。開發套件包含減少啟動時間的變更，本主題結束時會討論這些變更。

首先，本主題著重於您可以進行的變更，以減少冷啟動時間。這包括在您的程式碼結構和服務用戶端的組態中進行變更。

## 使用 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)同步案例使用 ，[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtAsyncHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtAsyncHttpClient.html)非同步案例則使用 。

本指南中的[設定 AWS CRT 型 HTTP 用戶端](http-configuration-crt.md)主題說明使用 HTTP 用戶端的優點、如何新增相依性，以及如何設定服務用戶端使用它們。

## 移除未使用的 HTTP 用戶端相依性
<a name="lambda-quick-remove-deps"></a>

除了明確使用 AWS CRT 型用戶端之外，您還可以移除軟體開發套件預設引入的其他 HTTP 用戶端。當需要載入的程式庫較少時，Lambda 啟動時間會縮短，因此您應該移除 JVM 需要載入的任何未使用成品。

Maven `pom.xml` 檔案的下列程式碼片段顯示排除 Apache 型 HTTP 用戶端和 Netty 型 HTTP 用戶端。（當您使用 AWS CRT 型用戶端時，不需要這些用戶端。) 此範例會從 S3 用戶端相依性中排除 HTTP 用戶端成品，並新增`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`方法。這會捷徑化軟體開發套件的預設[區域查詢程序](region-selection.md#default-region-provider-chain)，以檢查多個位置 AWS 區域 的資訊。  
若要讓 Lambda 程式碼獨立於 區域，請在 `region`方法中使用下列程式碼。此程式碼會存取 Lambda 容器設定`AWS_REGION`的環境變數。  

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

**使用 `EnvironmentVariableCredentialProvider`**  
就像區域資訊的預設查詢行為一樣，軟體開發套件會在多個位置尋找登入資料。透過在建置服務用戶端[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 或其他系統上運作。  
如果您打算在某個時間點使用 [Lambda SnapStart 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 架構，建議您使用輕量型 DI 架構，例如 [Dagger](https://dagger.dev/dev-guide/)。

## 使用 Maven 原型目標 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/)。

## 考慮 Lambda SnapStart for Java
<a name="lambda-quick-snapstart"></a>

如果您的執行時間需求相容， AWS 請提供適用於 [Java 的 Lambda SnapStart](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 的 開發套件 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 的一部分
+ 使用 [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 位址，稱為*存留時間* (TTL)。

由於 AWS 資源使用偶爾變更的 DNS 名稱項目，我們建議您將 JVM 設定為 5 秒的 TTL 值。這可確保當資源的 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，請設定 [networkaddress.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 8 的 `$JAVA_HOME/jre/lib/security/java.security` 檔案中設定 `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 網路屬性](https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html)參考中記載的 JDK 內部屬性，做為「未來版本可能不支援」的私有屬性。盡可能使用選項 1-2。

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

HTTP/2 是 HTTP 通訊協定的主要修訂版。這個新版本提供多種增強功能，可提升以下效能：
+ 二進位資料編碼提供更有效率的資料傳輸。
+ 標頭壓縮可減少用戶端下載的額外位元組，有助於用戶端更快取得內容。非常適合用於已受限於頻寬的行動用戶端。
+ 雙向非同步通訊 （多工） 允許用戶端與 之間的多個請求和回應訊息透過單一連線同時 AWS 傳輸，而不是透過多個連線來改善效能。

升級到最新開發套件的開發人員，如果其合作的服務支援 HTTP/2，便會自動使用 HTTP/2。新的程式設計介面可順暢地運用 HTTP/2 功能，並提供建置應用程式的新方式。

 適用於 Java 的 AWS SDK 2.x 為實作 HTTP/2 通訊協定的事件串流提供新的 APIs。如需如何使用這些新 APIs的範例，請參閱[使用 Kinesis](examples-kinesis.md)。