AWS参数存储:AWSSimpleSystemsManagementException:超出速率

时间:2019-04-01 09:27:43

标签: amazon-web-services aws-sdk aws-parameter-store

此刻,我需要在AWS Parameters Store中检索某些参数的最后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个参数都会花费6次对SSM的查询

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

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

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

例如,如果涉及到3个实例,它将在不到一秒钟的时间内执行18个请求,并且我会遇到错误(注意:我不完全知道同时打此代码的实例数量是否是3,这仅是为了说明您在某些时候遇到了错误)

所以我有2个问题:

首先,有没有一种方法可以首先检索参数的最新版本?

这样,我将处理一半的请求,这样我就很少遇到问题了!

第二,如何自动重试限制错误?

我发现此AWS Blog [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这样的共享内存缓存中!

1 个答案:

答案 0 :(得分:0)

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

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

TL; DR:之所以可行,是因为该方法只对SSM(每个lambda)发出1个请求(而不是6个),这要归功于GetParametersByPath递归而不是GetParameterHistory。

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

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

它以前是这样工作的:我每小时旋转一次秘密,我针对最近的10个版本解密令牌->如果用户发送的令牌的寿命早于11个小时或更长时间,我将无法对其进行解密。

现在,我不再有多个版本的单个参数,而是拥有多个参数,并且每个参数的最新版本都可以安全使用。

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

/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个秘密,而只需检查令牌中包含的一个秘密。

请注意,我以前可以做过这样的改进(通过发送令牌中未加密的版本号),而我以前从未想到过。

希望能帮助到某人