Amazon SQS 触发 AWS Lambda 及重试/DLQ

Amazon 在 2018 年 6 月份宣布可以设置用 SQS 来触发 Lambda,SQS 不再是单纯用于 ECS 服务中,或用于伸缩控制的。这儿就来亲自尝试一下用 SQS 驱动的 Lambda,以及要注意的要素。

首先使用 Java 编写 Lambda 的话,AWS 在 com.amazonaws:aws-lambda-java-events:2.20 版本开始加入了 com.amazonaws.services.lambda.runtime.events.SQSEvent 类,可是这个版本的 aws-lambda-java-events 是有所限的,因为 SQSEvent.SQSMessage 类是私有的,这就造成不能获取到 SQSEvent 中的记录数据。

//下面的操作代码无法编译,因为 SQSEvent.SQSMessage 是私有的,不可访问
SQSEvent.SQSMessage sqs = sqsEvent.getRecords().get(0); 
sqsEvent.getRecords().get(0).getBody();

Java 使用 SQS 来驱动 Lambda 的话,至少需要 com.amazonaws:aws-lambda-java-events:2.2.1 版本,从此 SQSEvent.SQSMessage 变成 public 了。该版本是于  2018 年 6 月传到 Maven 官方中央仓库的,这就是那时才能真正用来写 Java 的 SQS 触发的 Lambda.

同时此篇也是作为上文 AWS Lambda 重试与死信队列(DLQ) 的一个很重要的补充。在此也会验证 SQS 触发的 Lambda 的重试机制以及 DLQ 相关的内容。

创建两个  SQS 队列

test-sqs-queue 用于触发 Lambda

test-sqs-dlq-queue 不能被 Lambda  触发的消息希望达到最大重试次数后转到该死信队列中去

Lambda 代码

想法是希望在处理 SQS 中的消息时,如果消息体中含有 dlq 字符串就抛出异常,重试若干次后转入到 test-sqs-dlq-queue 中去。

部署 Lambda

打成可部署的 Lambda jar 包自是不必说,主要强调的是关于执行 Lambda role 和 SQS 的超时设置

该 Lambda 如果设置由 test-sqs-queue 来触发(可以设置 batch size 大小),那么执行该 Lambda 的 role 必须要有针对 SQS 主题 test-sqs-queue 的以下三个权限

sqs:ReceiveMessage     //Lambda 由 SNS 触发是不需要特别的 SNS 相关权限
sqs:DeleteMessage       //Lambda 执行成功后会删除处理过的消息
sqs:GetQueueAttributes

并且该 SQS 主题 test-sqs-queue 的超时 Default Visibility Timeout 不得小于该 Lambda 的 Timeout 设置。因为不希望 Lambda 正在处理消息,还没有 Timeout 之前消息早已回到了源 SQS 队列去了,会造成一条消息被成功处理多次。

同时设置该 Lambda 的 DLQ 为 test-sqs-dlq-queue。下面的测试实际可看到由  SQS 触发的 Lambda,设置 DLQ 是没有意义的。

测试用 SQS 驱动 Lambda

我们往 test-sqs-queue 中放入一条消息,内容为 hello dlq exception, 使得 Lambda 抛出异常。我们立马可以看到 Lambda 被 SQS 触发,并且每次执行都抛出异常,而且该消息始终无法从队列 test-sqs-queue 中清除掉。

连续观察一段时间,看到该 Lambda 每隔一分钟(由于 test-sqs-queue 设置的 Default Visibility Timeout 是 60 秒)重试一次,永不停歇。因此即例设置了该 Lambda 的 DLQ, 该消息都没有机会送入到 Lambda 的 DLQ 中去。

每分钟重试一次是因为 test-sqs-queue 设置的 Default Visibility Timeout 是 60 秒,所以 Lambda 取出消息(Message In Flight),处理出现异常,不能删除该消息,一分钟后该消息又变为  Available。再取出消息处理,异常,消息回队列,周而复始,每次在消息在外面呆一分钟后又回队。

如果设置 test-sqs-queue 的 Default Visibility Timeout 为 2 分钟,那么就是每 2 分钟重试一次,一直持续下去。

我们也可以测试一个能正常处理的逻辑,把 test-sqs-quque 清空掉,并发一条内容为  hello 的消息,我们会发现 Lambda  处理完马上该消息从队列中消失了。

SQS 驱动的 Lambda 的重试与 DLQ 设置

用 SQS 驱动的 Lambda 仍然可以为 Lambda 设置 DLQ,但是这个 DLQ 设置是无效的了,并且是一个陷阱,如果没有为源 SQS 队列设置 DLQ 的情况下,该 Lambda 的 DLQ 设置会造成无限重试,直到消息失效为止, 默认为 4  天,这期间如果是按默认的 Default Visibility Time 30 秒重试一次,那么 Lambda 被调用的费用也相当可观。

所以一定要注意:使用 SQS 来驱动 Lambda 的话,千万不要设置 Lambda 的 DLQ,而应当设置源 SQS 队列的 DLQ。

如下图

针对源 SQS 队列 test-sqs-queue, 设置重试 3 次后消息转移到 DLQ(Dead Letter Queue) 队列 test-sqs-dlq-queue, 从此该消息不再触发 Lambda 了,后续自行额外处理。

SQS 触发器的 Batch Size

SQS 的触发器也有像 Kinesis 触发器那样的 Batch Size, 它们的含义是类似的,也是决定了 sqsEvent.getRecords() 中最大的记录数。至于并发的 Lambda 实例数目尚不清楚。

现在我们可进步测试 Lambda 取到 SQS 多条记录时,抛出异常时怎么处理消息的。为此我们把上面的 process 方法改动一下

Batch 中只要含有一条消息有 dlq 字样的就抛出异常,然后设置 SQS 触发器的  Batch Size 为 10, 再往队列 test-sqs-queue 放入以下 16 条消息

a, dlq, b, dlq, c, dlq, d, dlq, e, dlq, f, dlq, f, dlq, g, dlq

SQS 触发的 Lambda 处理 SQS 消息的机制是,如果 Lambda 能正常处理所有取到的消息后,就把它们从队列中全部删除,如果有任意的异常发生,那么它们全部重新归队。其中某个消息达到最大的重试次数就进到所设置 SQS DLQ 中去,这很容易棒杀正常的消息。

比如按照上面的 test-sqs-queue 的 DLQ 设置,并且 SQS 触发器的 Batch Size 为 2 的情况下

  1. 第一轮:(a, dlq), (b, dlq), (c), (dlq, d), e        :  搭上 dlq 的第 1, 2, 4 组消息会失败,所以它们又回到 test-sqs-queue 队列
  2. 第二轮:(a), (dlq, b), (dlq, dlq):                      : 含有 dlq 的第  2, 3 组消息会失败,它们又回 test-sqs-queue 队列中去
  3. 第三轮:  (dlq, b), (dlq, dlq):                              : 所有消息都要回到 test-sqs-queue, 因为它们都含有 dlq 字符串

上面最冤枉的莫过于消息 b了,即便是良民,因为每次没跟对人,最后的下场与坏人没有分别,被送到了 DLQ test-sqs-dlq-queue 去了。要是在第一轮中 b 被正常处理过一次或许也无妨,最终 b 跑到了 test-sqs-dlq-queue 也不打紧。

关于 SQS 触发器,Batch Size 是一个值得当心的地方,Batch Size 为 1 自然是最安全的,但效率会是个问题。也许必要时我们可以考虑在处理完 Batch 中的某一条消息后手工从源 SQS 队列中把该条消息删除掉,由 SQSMessage 是能够获得 receiptHandle 的。

习惯性小结一下

对于使用 SQS 来触发 Lambda, 以下几点强调一下

  1. 执行 Lambda 的 role 需要用对于源 SQS 的 ReceiveMessage, DeleteMessage 和  GetQueueAttributes 权限
  2. 源  SQS 的 Default Visibility Timeout 设置不能小于 Lambda 的超时设置
  3. Lambda 会依照源 SQS 的 Default Visibility Timeout 的间隔时间进行重试,直到消息被移除源队列
  4. 不能再使用 Lambda 的 DLQ 设置了,会造成无限重试,应该对源 SQS 队列直接设置 DLQ
  5. SQS 触发器的 Batch Size 需多留意,可能会因为 Batch 中的某条消息产生异常而影响正常的消息处理

类别: AWS. 标签: , . 阅读(42). 订阅评论. TrackBack.

Leave a Reply

avatar