

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

# 在中重试 适用于 Kotlin 的 AWS SDK
<a name="retries"></a>

调用 AWS 服务 偶尔会返回意外异常。如果重试调用，某些类型的错误（例如节流或暂时错误）可能会成功。

本页介绍如何自动 适用于 Kotlin 的 AWS SDK 处理重试以及如何为您的应用程序自定义重试行为。

## 了解重试行为
<a name="retries-understanding"></a>

以下各节说明了 SDK 如何确定何时重试请求以及哪些异常被视为可重试。

### 默认重试配置
<a name="retries-default"></a>

默认情况下，每个服务客户端都会自动配置[标准重试策略](https://docs.aws.amazon.com/smithy-kotlin/api/latest/runtime-core/aws.smithy.kotlin.runtime.retries/-standard-retry-strategy/index.html)。默认配置最多会尝试失败三次的呼叫（初次尝试加上两次重试）。每次呼叫之间的中间延迟配置为指数退避和随机抖动，以避免重试风暴。此配置适用于大多数用例，但在某些情况下可能不适合，例如高吞吐量系统。

SDK 仅在出现可重试错误时才会尝试重试。可重试错误的示例包括套接字超时、服务端节流、并发或乐观锁失败以及暂时性服务错误。参数缺失或无效、 authentication/security 错误和配置错误异常不被视为可重试。

您可以通过设置最大尝试次数、延迟和退避次数以及令牌桶配置来自定义标准重试策略。

### 哪些异常是可以重试的？
<a name="retries-default-policy-details"></a>

 适用于 Kotlin 的 AWS SDK 使用预配置的重试策略来确定哪些异常是可重试的。服务客户端配置具有一个`retryPolicy`属性，用于指定应用于重试的策略。如果未指定自定义值，则默认值为[AwsRetryPolicy](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/aws-http/aws.sdk.kotlin.runtime.http.retries/-aws-retry-policy/)。

以下异常通过以下方式确定为可重试：`AwsRetryPolicy`

#### 可通过错误代码重试
<a name="retries-retryable-by-error-code"></a>

任何`ServiceException`具有以下值的`sdkErrorMetadata.errorCode`任意：
+ `BandwidthLimitExceeded`
+ `EC2ThrottledException`
+ `IDPCommunicationError`
+ `LimitExceededException`
+ `PriorRequestNotComplete`
+ `ProvisionedThroughputExceededException`
+ `RequestLimitExceeded`
+ `RequestThrottled`
+ `RequestThrottledException`
+ `RequestTimeout`
+ `RequestTimeoutException`
+ `SlowDown`
+ `ThrottledException`
+ `Throttling`
+ `ThrottlingException`
+ `TooManyRequestsException`
+ `TransactionInProgressException`

#### 可通过 HTTP 状态码重试
<a name="retries-retryable-by-status-code"></a>

任何`ServiceException`具有以下值的`sdkErrorMetadata.statusCode`任意：
+ 500（内部服务错误）
+ 502（错误的网关）
+ 503（服务不可用）
+ 504（网关超时）

#### 可按错误类型重试
<a name="retries-retryable-by-error-type"></a>

任何`ServiceException`具有以下值的`sdkErrorMetadata.errorType`任意：
+ `ErrorType.Server`（例如内部服务错误）
+ `ErrorType.Client`（例如请求无效、未找到资源、访问被拒绝等）

#### 可通过 SDK 元数据重试
<a name="retries-retryable-by-metadata"></a>

任何`SdkBaseException`地方：
+ `sdkErrorMetadata.isRetryable`是`true`（例如客户端超时、 networking/socket 错误等）
+ `sdkErrorMetadata.isThrottling`是`true`（例如在短时间内发出太多请求）

有关每个服务客户端可能抛出的异常的完整列表，请参阅[特定于服务的 API 参考文档](https://docs.aws.amazon.com/#products)。

### 检查异常是否可以重试
<a name="retries-check-exception-retryable"></a>

要确定 SDK 是否认为异常可重试，请检查捕获到的异常的`isRetryable`属性：

```
try {
    dynamoDbClient.putItem {
        tableName = "MyTable"
        item = mapOf("id" to AttributeValue.S("123"))
    }
} catch (e: SdkBaseException) {
    println("Exception occurred: ${e.message}")
    
    if (e.sdkErrorMetadata.isRetryable) {
        println("This exception is retryable - SDK will automatically retry")
        println("If you're seeing this, retries may have been exhausted")
    } else {
        println("This exception is not retryable - fix the underlying issue")
        
        // Common non-retryable scenarios.
        when {
            e.message?.contains("ValidationException") == true -> 
                println("Check your request parameters")
            e.message?.contains("AccessDenied") == true -> 
                println("Check your IAM permissions")
            e.message?.contains("ResourceNotFound") == true -> 
                println("Verify the resource exists")
        }
    }
}
```

### 当重试失败时，你的代码会出现哪些异常
<a name="retries-exception-types-during-retries"></a>

当 SDK 的重试机制无法解决问题时，您的应用程序代码就会抛出异常。了解这些异常类型有助于您实现适当的错误处理。这些*不是*触发重试的例外情况，而是由 SDK 内部处理的。

当重试用尽或禁用时，你的代码将捕获以下类型的异常：

重试耗尽后的服务异常  
当所有重试尝试都失败时，您的代码会捕获导致最后一次重试失败的最终服务异常（的子类`AwsServiceException`）。这可能是限制错误、服务器错误或其他特定于服务的异常，SDK 无法通过重试解决这些异常。

重试耗尽后的网络异常  
当网络问题在所有重试尝试中仍然存在时，您的代码会捕获连接超时、DNS 解析失败以及 SDK 无法解决的其他连接问题等问题的`ClientException`实例。

使用以下模式来处理应用程序中的这些异常：

```
try {
    s3Client.getObject { 
        bucket = "amzn-s3-demo-bucket"
        key = "my-key" 
    }
} catch (e: AwsServiceException) {
    // Service-side errors that persisted through all retries.
    println("Service error after retries: ${e.errorDetails?.errorCode} - ${e.message}")
    
    // Handle specific service errors that couldn't be resolved.
    if (e.errorDetails?.errorCode == "ServiceQuotaExceededException" || 
        e.errorDetails?.errorCode == "ThrottlingException") {
        println("Rate limiting persisted - consider longer delays or quota increase")
    }
} catch (e: ClientException) {
    // Client-side errors (persistent network issues, DNS resolution failures, etc.)
    println("Client error after retries: ${e.message}")
}
```

## 自定义重试行为
<a name="retries-customizing"></a>

以下各节介绍如何针对您的特定用例自定义 SDK 的重试行为。

### 配置最大尝试次数
<a name="retires-max-attempts"></a>

在构造客户端期间，您可以自定义 [`retryStrategy`DSL 区块](https://docs.aws.amazon.com/smithy-kotlin/api/latest/runtime-core/aws.smithy.kotlin.runtime.retries/-standard-retry-strategy/-config/-builder/index.html)中的默认最大尝试次数 (3)。

```
val dynamoDb = DynamoDbClient.fromEnvironment {
    retryStrategy {
        maxAttempts = 5
    }
}
```

上一个片段中显示了 DynamoDB 服务客户端，SDK 最多会尝试五次失败的 API 调用（初次尝试加上四次重试）。

您可以通过将最大尝试次数设置为一次来完全禁用自动重试，如以下代码段所示。

```
val dynamoDb = DynamoDbClient.fromEnvironment {
    retryStrategy {
        maxAttempts = 1  // The SDK makes no retries.
    }
}
```

### 配置延迟和退缩
<a name="retries-delays-backoff"></a>

如果需要重试，默认的重试策略会在进行下一次尝试之前等待一段时间。第一次重试的延迟时间很短，但后续重试的延迟时间会呈指数级增长。最长延迟时间是有上限的，以防延迟时间过长。

最后，随机抖动会应用于所有尝试之间的延迟。抖动有助于降低可能导致重试风暴的大量重试的影响。（有关指数回退和抖[动的更深入讨论，请参阅这篇AWS 架构博客文章](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)。）

延迟参数可在 [`delayProvider`DSL 模块](https://docs.aws.amazon.com/smithy-kotlin/api/latest/runtime-core/aws.smithy.kotlin.runtime.retries.delay/-exponential-backoff-with-jitter/-config/index.html)中进行配置。

```
val dynamoDb = DynamoDbClient.fromEnvironment {
    retryStrategy {
        delayProvider {
            initialDelay = 100.milliseconds
            maxBackoff = 5.seconds
        }
    }
}
```

使用上一个片段所示的配置，客户端将第一次重试尝试最多延迟 100 毫秒。任何重试尝试之间的最大间隔为 5 秒。

以下参数可用于调整延迟和退缩。


| 参数 | 默认 值 | 说明 | 
| --- | --- | --- | 
| initialDelay | 10 毫秒 | 第一次重试的最大延迟时间。施加抖动时，实际延迟量可能会更少。 | 
| jitter | 1.0（完全抖动） |  随机减少计算延迟的最大振幅。默认值为 1.0 意味着计算出的延迟可以减少到不超过 100% 的任意值（例如，降至 0）。值为 0.5 意味着计算出的延迟最多可以减少一半。因此，10 毫秒的最大延迟可以减少到 5 毫秒到 10 毫秒之间的任何值。值为 0.0 表示不施加任何抖动。  ️ 抖动配置是一项高级功能。通常不建议自定义此行为。   | 
| maxBackoff | 20 秒 | 适用于任何尝试的最大延迟时间。设置此值可以限制在后续尝试之间发生的指数级增长，并防止计算出的最大值过大。此参数限制了在应用抖动之前计算出的延迟。如果应用抖动，则可能会进一步缩短延迟。 | 
| scaleFactor | 1.5 | 随后的最大延迟将增加的指数基数。例如，假定 a 为 `initialDelay` 10ms，a 为 `scaleFactor` 1.5，则将计算出以下最大延迟：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/sdk-for-kotlin/latest/developer-guide/retries.html)当施加抖动时，每次延迟的实际量可能会更少。 | 

### 配置重试令牌存储桶
<a name="retries-token-bucket"></a>

您可以通过调整默认令牌桶配置来进一步修改标准重试策略的行为。重试令牌存储桶有助于减少不太可能成功或可能需要更多时间才能解决的重试，例如超时和限制失败。

**重要**  
令牌存储桶配置是一项高级功能。通常不建议自定义此行为。

每次重试尝试（可选包括初次尝试）都会减少令牌桶中的一些容量。减少的金额取决于尝试的类型。例如，重试瞬态错误可能很便宜，但重试超时或限制错误可能会更昂贵。

成功尝试将容量返回到存储桶。存储桶的增量不得超过其最大容量，也不得减至零以下。

根据`useCircuitBreakerMode`设置的值，尝试将容量减少到零以下会导致以下结果之一：
+ 如果设置为 TRUE，则会引发异常 — 例如，如果重试次数过多，并且不太可能成功更多的重试。
+ 如果设置为 FALSE，则会有延迟，例如，延迟到存储桶再次有足够的容量。

**注意**  
当断路器激活（令牌桶容量为零）时，SDK 会抛出 “已`ClientException`超出重试容量” 消息。这是客户端异常，而不是`AwsServiceException`，因为它源于 SDK 的重试逻辑而不是服务。 AWS 无需尝试操作即可立即抛出异常，这有助于防止服务中断期间的重试风暴。

令牌桶参数可在 [`tokenBucket`DSL 区块](https://docs.aws.amazon.com/smithy-kotlin/api/latest/runtime-core/aws.smithy.kotlin.runtime.retries.delay/-standard-retry-token-bucket/-config/index.html)中配置：

```
val dynamoDb = DynamoDbClient.fromEnvironment {
    retryStrategy {
        tokenBucket {
            maxCapacity = 100
            refillUnitsPerSecond = 2
        }
    }
}
```

以下参数可用于调整重试令牌存储桶：


| 参数 | 默认 值 | 说明 | 
| --- | --- | --- | 
| initialTryCost | 0 | 初始尝试时要从存储桶中减少的金额。默认值 0 表示容量不会减少，因此初始尝试不会停止或延迟。 | 
| initialTrySuccessIncrement | 1 | 初始尝试成功时要增加容量的量。 | 
| maxCapacity | 500 | 令牌桶的最大容量。可用代币的数量不能超过此数字。 | 
| refillUnitsPerSecond | 0 | 每秒向存储桶重新添加的容量。值为 0 表示不会自动重新添加任何容量。（例如，只有成功的尝试才会增加容量）。如果值为 0，则必须useCircuitBreakerMode为 TRUE。 | 
| retryCost | 5 | 暂时失败后尝试从存储桶中减少的金额。如果尝试成功，则将相同的金额重新递增回存储桶。 | 
| timeoutRetryCost | 10 | 超时或限制失败后尝试从存储桶中减少的金额。如果尝试成功，则将相同的金额重新递增回存储桶。 | 
| useCircuitBreakerMode | TRUE | 确定尝试减少容量会导致存储桶容量降至零以下时的行为。如果为 TRUE，则令牌存储桶将抛出一个异常，表示不再存在重试容量。如果为 FALSE，则令牌存储桶将延迟尝试，直到重新填充了足够的容量。 | 

有关在重试场景中引发的异常类型（包括断路器异常）的详细信息，请参阅[当重试失败时，你的代码会出现哪些异常](#retries-exception-types-during-retries)。

### 配置自适应重试
<a name="retries-adaptive-retries"></a>

作为标准重试策略的替代方案，自适应重试策略是一种高级方法，它寻求理想的请求速率以最大限度地减少限制错误。

**重要**  
自适应重试是一种高级重试模式。通常不建议使用这种重试策略。

自适应重试具备标准重试的所有功能。它添加了一个客户端速率限制器，用于衡量节流请求与非节流请求的比率。它还会限制尝试流量以保持在安全带宽内，理想情况下会使节流错误为零。

该速率会根据不断变化的服务条件和流量模式实时调整，并可能相应地增加或减少流量速率。至关重要的是，在高流量场景中，速率限制器可能会延迟初始尝试。

您可以通过为`retryStrategy`方法提供附加参数来选择自适应重试策略。速率限制器参数可在 [`rateLimiter`DSL](https://docs.aws.amazon.com/smithy-kotlin/api/latest/runtime-core/aws.smithy.kotlin.runtime.retries.delay/-adaptive-rate-limiter/-config/index.html) 模块中进行配置。

```
val dynamoDb = DynamoDbClient.fromEnvironment {
    retryStrategy(AdaptiveRetryStrategy) {
        maxAttempts = 10
        rateLimiter {
            minFillRate = 1.0
            smoothing = 0.75
        }
    }
}
```

**注意**  
自适应重试策略假设客户端使用单个资源（例如，一个 DynamoDB 表或一个 Amazon S3 存储桶）。  
如果您使用单个客户端处理多个资源，则当客户端访问所有其他资源时，与一个资源相关的节流或中断会导致延迟增加和出现故障。当您使用自适应重试策略时，我们建议您为每种资源使用一个客户端。