首页 > 解决方案 > AWS 参数存储:AWSSimpleSystemsManagementException:超出速率

问题描述

目前,我需要在 AWS 参数存储中检索一些参数的最后 10 个值

我在 kotlin 中使用以下代码:

    val p1 = retrieveAllValidVersions("P1")
    val p2 = retrieveAllValidVersions("P2")
    val p3 = retrieveAllValidVersions("P3")

这是retrieveAllValidVersions的代码

    private fun retrieveAllValidVersions(paramName: String): List<ParameterHistory> {
        val res = mutableListOf<ParameterHistory>()
        val ssmClient = AWSSimpleSystemsManagementClientBuilder.defaultClient()
        var nextToken : String? = null
        do {
            val ssmParams = ssmClient.getParameterHistory(GetParameterHistoryRequest()
                    .withName(paramName)
                    .withWithDecryption(true)
                    .withNextToken(nextToken)
            )
            res.addAll(ssmParams.parameters)
            nextToken = ssmParams.nextToken
        } while (nextToken != null)


        return validVersions.sortedByDescending { it.version }.take(10)
    }

https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#limits_ssm中所述,参数的最大版本数为 100

正如https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetParameterHistory.html所述,您只能在 maxResults 中检索 50 个值,因此我需要为每个参数调用 2 次(因为我有超过 50 个版本)

因此,每次检索我的 3 个参数都需要对 SSM 进行 6 次查询

我将每个参数的最后 10 个值缓存在内存中 5 分钟

问题是,当我的 lambdas 的多个实例同时过期其缓存时,它们会同时进行检索并且

com.amazonaws.services.simplesystemsmanagement.model.AWSSimpleSystemsManagementException: Rate exceeded (Service: AWSSimpleSystemsManagement; Status Code: 400; Error Code: ThrottlingException; Request ID: xxx)

例如,如果涉及 3 个实例,它将在不到一秒的时间内完成 18 个请求,我会遇到错误(注意:我不知道同时命中此代码的实例数是否为 3,这只是一个猜测来说明您在某些时候遇到了错误)

所以我有两个问题:

首先,有没有办法首先检索参数的最新版本?

这样,我将完成一半的请求,这样我就不会经常遇到问题了!

其次,如何自动重试节流错误?

我发现这篇 AWS 博客 [1] 帖子说我必须解析错误消息,但这是一篇旧帖子(2013 年),这非常难看(AWS 更改消息的那一刻,所有机制都崩溃了)!

[1]:https ://aws.amazon.com/fr/blogs/messaging-and-targeting/how-to-handle-a-throttling-maximum-sending-rate-exceeded-error/

最后一点:我正在使用带有“自动加密”和 IAM 的参数存储,我不想将参数存储在我自己的数据库中,也不想将它们缓存在像 redis 这样的共享内存缓存中!

标签: amazon-web-servicesaws-sdkaws-parameter-store

解决方案


我找到了一个解决方案/解决方法,我将在这里分享。

如果您找到更好的方法,请随时发表评论!

TL;DR:解决方法有效,因为它只对 SSM(每个 lambda)发出 1 个请求,而不是 6 个,这要归功于 GetParametersByPath 递归而不是 GetParameterHistory。

因此,为了简化,我的用例是存储一个秘密来加密一个令牌并能够使用它 10 个小时。

注意:IRL,我使用 3 个不同的秘密,因此问题中的 P1、P2、P3。在下文中,我将简化并仅讨论 1 个秘密,因为它对任意数量的秘密都具有类似的作用(直到您达到 SSM 中的最大参数数量,即 10K ...)

它以前的工作方式是这样的:我每小时轮换一次秘密,我根据最近 10 个版本解密令牌 -> 如果用户发送了 11 小时或更长时间的令牌,我将无法再解密它。

现在,我不再使用具有多个版本的单个参数,而是拥有多个参数,每个参数只有最后一个版本可以安全使用。

我的 SSM Parameter Store 以前看起来像(对于每个参数)

/secret/P1

现在看起来像

/secrets/P1/s1 -> a secret
/secrets/P1/s2 -> a secret
...
/secrets/P1/s10 -> a secret
/secrets/P1/current -> s4 

我的代码现在使用递归执行 GetParametersByPath("/secrets") 以在一个请求中检索所有参数(即 P1、P2、P3)的所有有效秘密。

所以每个 lambda 都会做一个请求,几乎没有机会达到 RateExceeded。

当客户端发送令牌时,我尝试根据 10 个当前秘密进行解密。

秘密轮换已更改为:检索 /current 并更改下一个(即如果 current 是 s4,我们更改 s5 并将 /current 设置为 s5)

最后一点,我还实现了一项改进,即将 /current 添加到令牌中(未加密)。

这样做,我不需要检查这 10 个秘密,只需要检查令牌中包含的一个。

请注意,我之前本可以进行这种改进(通过在令牌中发送未加密的版本号),我之前只是没有想到它。

希望对某人有所帮助


推荐阅读