宣布适用于 Kotlin 的 亚马逊云科技 开发工具包中可重写的客户端配置

作者: 伊恩·博茨福德 | 202

我们很高兴地宣布,适用于 K otlin 的 亚马逊云科技 开发工具包 现在支持 可重写的客户端 配置。 您可以使用此功能使用与客户端初始化时提供的配置不同的专用配置执行 亚马逊云科技 服务调用。这可以解锁新功能并提高代码的灵活性。在这篇文章中,我将讨论这些功能,展示代码示例,并解释一些新的最佳实践。让我们潜水吧!

初始化客户端

所有服务客户端都需要在初始化时进行配置。如果您没有明确提供配置,则可能会通过 共享配置文件 、系统属性 、环境变量或其他来源从运行环境中检测到配置。无论是明确指定还是隐式检测,服务客户端在初始化后都会在内部跟踪其所有必需的配置。

让我们来看一个亚马逊 DynamoDB 的例子:

DynamoDbClient.fromEnvironment {
    // explicit config
    logMode = LogMode.LogResponse
    region = "us-west-2"
}.use { ddbClient ->

    // print all table names
    ddbClient.listTables().tableNames?.forEach(::println)

    // print first page of items from "dictionary" table
    ddbClient.scan { tableName = "dictionary" }.items?.forEach(::println)
}

在前面的示例中,我明确设置了 日志模式 亚马逊云科技 区域 。然后,客户端确定其余配置字段的值,例如 证书提供程序 (通常是 默认的凭证提供者链 )、 重试策略 (通常是 标准重试策略 )等。

我从 DDbClient 执行的每个 亚马逊云科技 服务调用 (例如, ListTables 扫描 调用)都将使用初始化时解析的相同配置。通常这没问题,因为通常我想使用相同的证书在同一个区域拨打电话。

但是,有时我可能想使用略有不同的配置执行一些服务调用。为此,我将在服务客户端上使用 withConfig 方法。让我们来看几个例子:

更改重试行为

我想将一堆项目批量插入到 DynamoDB 表中,所以我写了一个新函数供前面的示例使用:

suspend fun bulkLoadWords(ddbClient: DynamoDbClient, fromFile: File) {
    fromFile
        .readLines()
        .windowed(25) // 25 is the max number of DynamoDB batch items
        .forEach { page ->
            ddbClient.batchWriteItem {

                // transform file lines to WriteRequest elements
                val items = page.map { word ->
                    WriteRequest {
                        putRequest {
                            item = mapOf(
                                "language" to AttributeValue.S("en"),
                                "word" to AttributeValue.S(word),
                            )
                        }
                    }
                }

                requestItems = mapOf("dictionary" to items)
            }
        }
}

如果我的表的 写入容量 太低并且项目插入速度太快,我可能会看到限制异常。适用于 Kotlin 的 亚马逊云科技 开发工具包 默认使用 标准重试策略 ,该策略最多尝试调用 3 次。对于批量插入调用,我可以在调用 Bulk LoadWords 之前将尝试次数增加到 10 次:

// using a client with more retries...
ddbClient.withConfig {
    retryStrategy { maxAttempts = 10 }
}.use { patientDdbClient ->

    // ...bulk insert items from file
    bulkLoadWords(patientDdbClient, File("/tmp/words.txt"))
}

使用 withConfig 允许我保留现有 DdbClient 的设置 (特别是日志模式和区域),但可以覆盖特定的设置(在本例中为重试尝试)。 withConfig 函数返回一个新的客户端实例,使用函数允许我将重写的客户端的生命周期范围限定为一个小块。也就是说,当使用区块结束时, PatientddbClient 将关闭。

启用其他日志记录

有时,出于审计或调试目的启用服务调用子集的 额外日志 可能很有用。

让我们来看一个使用 亚马逊云科技 Lambda 的示例:

LambdaClient.fromEnvironment {
    region = "us-west-2"
}.use { lambdaClient ->

    // invoke "sum" function
    lambdaClient.invoke {
        functionName = "sum"
        payload = """ { "x": 2, "y": 3 } """.toByteArray()
    }

    // using a client with more logging...
    lambdaClient.withConfig {
        logMode = LogMode.LogRequest + LogMode.LogResponse
    }.use { loggingLambdaClient ->

        // ...invoke "divide" function
        loggingLambdaClient.invoke {
            functionName = "divide"
            payload = """ { "x": 5, "y": 0 } """.toByteArray()
        }
    }
}

在前面的示例中,我的第一个 Lambda 调用 使用了 LambdaClient 的配置。 第二个 invoke 调用 使用被覆盖的配置,该配置设置了更详细的线路记录模式。

切换区域

亚马逊云科技 服务调用几乎总是定向到 区域终端节点 。因此,服务客户端始终初始化为某个区域,要么是显式提供的,要么是从环境中隐式检测到的(例如,在 亚马逊云科技 Lambda 或 Amazon EC2 实例上运行时,这些实例本身就是区域化的)。但是,有时切换区域以访问位于不同区域的资源可能会很有用。

让我们举一个使用亚马逊 Simple Storage Service (Amazon S3) 的示例:

data class Upload(val fileName: String, val region: String, val bucket: String)

val uploads = listOf(
    Upload("q3-report.pdf"    , "us-west-2", "quarterly-reports"),
    Upload("q4-report.pdf"    , "us-west-2", "quarterly-reports"),
    Upload("us-financials.zip", "us-east-1", "us-datasets"      ),
    Upload("eu-financials.zip", "eu-west-1", "eu-datasets"      ),
)

S3Client.fromEnvironment { 
    region = "us-west-2"
}.use { s3Client ->
    
    // upload the files from the list
    uploads.forEach { upload ->
        s3Client.withConfig { region = upload.region }.use { regionalS3Client ->
            val file = File("/tmp/${upload.fileName}")
            
            regionalS3Client.putObject { 
                bucket = upload.bucket
                key = upload.fileName
                body = ByteStream.fromFile(file)
            }
        }
    }
    
}

在前面的示例中,我首先定义了上传列表,其中包括有关文件的本地名称、应将其上传到的区域以及存储桶名称的信息。然后,我初始化一个 S3 客户端并遍历上传列表。对于每次上传,我 使用 withConfig 为所需区域创建的区域化 S3 客户端调用 p utObjec t。

最佳实践

重写客户端配置可以解锁一些强大的用例。以下是一些充分利用这些技巧的技巧:

  • 使用 withConfig , 而不是从头开始 创建其他客户端 。使用 FromEnVironment 创建新的服务客户端会检测来自本地环境的配置。创建过程可以从文件系统读取配置文件或进行 HTTP 调用以解析所需的配置值。尽管使用构造函数创建服务客户端不会检测到配置,但与使用 FromenVironment 一样,它仍然可以从文件系统读取。但是,这两种方法都无法重用来自已经运行的客户端的配置值。出于这个原因,当你已经有 配置的服务客户端可用时 ,我们建议使用 withConfig 来创建其他服务客户端。初始化速度要快得多,并且所有现有配置都将复制到新客户端,允许您仅指定不同之处。
  • 使用 use 函数限定客户端的范围 。调用 withConfig 会创建一个新的客户端实例。它比使用构造函数或 FromEnVironmen t 从头开始创建客户端要快, 但它会共享对某些长时间运行的资源的引用,例如证书提供程序、HTTP 客户端引擎等。请务必封装使用这些被覆盖的客户端, 以确保它们最后被正确关闭,从而腾出对共享资源的引 。或者,你可以直接在被覆盖的客户端 上调用 cl os e。

更多信息

现在你已经了解了如何重写客户端配置,我们希望你能在自己的代码中尝试一下!以下资源可以提供更多详细信息以帮助您入门:

  • 关于覆盖客户端配置的开发者指南条目
  • 适用于 Kotlin 的 亚马逊云科技 开发工具包中的配置选项

如果您对如何将此功能集成到代码中有任何疑问,请随时在下面发表评论或 在我们的 GitHub 存储库 中开始 讨论 。如果您遇到错误或文档不正确,请 提出问题 。最后,我们很想在 开发者调查 中听到你对 SDK 和这项新功能的 看法 !

作者简介:

Ian Botsford

伊恩·博茨福德

Ian 是一名开发人员,正在开发适用于 Kotlin 的 亚马逊云科技 开发工具包。他热衷于通过流畅的惯用软件开发工具包让 亚马逊云科技 易于使用。你可以在 GitHub 上找到他,网址 为 @ianbotsf


*前述特定亚马逊云科技生成式人工智能相关的服务仅在亚马逊云科技海外区域可用,亚马逊云科技中国仅为帮助您发展海外业务和/或了解行业前沿技术选择推荐该服务。