SQS 이벤트에서 트리거된 AWS Lambda의 오류 처리

24897 단어 sqsserverlessawslambda

이 글은 처음에 발표되었다 my blog
에서 SQS 이벤트를 통해 Lambda 함수를 트리거하는 새로운 기능을 설명합니다.
이 기능을 사용하면 Lambda 함수에서 SQS 메시지를 삭제할 필요가 없습니다. AWS가 이 작업을 수행하지만 Lambda가 성공적으로 끝났을 때만 이 기능을 사용할 수 있습니다.
만약 Lambda가 실패하면 메시지는 대기열에 유지되고'가시성 시간 초과'후에 Lambda 기능을 다시 볼 수 있습니다. 이 시간은 Amazon SQS가 다른 소비자들이 메시지를 수신하고 처리하는 것을 막는 시간입니다. (기본값은 30초)
SQS 대기열VisibilityTimeout 속성 생성 시 또는 특정 메시지(사용ChangeMessageVisibility call에서 이 시간 초과를 구성할 수 있습니다.
이러한 상황을 처리하지 않으면 다음과 같은 상황이 발생할 수 있습니다. Lambda가 한동안 실행되고 메시지가 실패할 때, AWS의 계산 능력과 느슨한 자금을 낭비하고, SQS 대기열은 공중에 떠 있는 미처리 메시지에 따라 증가합니다.
마지막으로, 모든 메시지는 만료 시간 후에 SQS 대기열에서 삭제됩니다 (속성 MessageRetentionPeriod. SQS 대기열을 만들 때 이 속성을 지정할 수 있습니다. 기본값은 4일). 그러나 발생해서는 안 됩니다.너는 반드시 스스로 국면을 통제해야 한다.
우리는 어떻게 그것을 통제합니까?
나는 적어도 두 가지 가능성을 생각했다.다른 것을 알고 있다면 댓글로 공유해 주세요.

고정 큐 또는 DLQ


DLQ는 실패한 메시지를 저장하는 데 사용되는 대기열의 이름입니다.기술적으로 SQS 대기열과 다를 바 없습니다.이것은 단지 대기열에 대한 용도일 뿐이다.메시지가 Lambda 함수에서 올바르게 처리되지 않을 때 DLQ로 전달하고 DLQ에서 단독 함수로 처리할 수 있습니다. 예를 들어 주 Lambda 함수로 다시 보내거나 관리자에게 알림을 보내거나 삭제만 할 수 있습니다.
여기에서 DLQ를 사용하는 응용 프로그램의 예를 보여 드리겠습니다.
이렇게 간단했으면 좋겠습니다. - 서버 없는 문서에 따라 Lambda 함수에만 onError 속성을 지정해야 합니다. 아래와 같습니다.
functions:
  receiver:
    handler: receiver.handler
    events:
      - sqs:
          arn:
            Fn::GetAtt:
              - MyQueue
              - Arn
    onError: arn:aws:sqs:us-east-1:811338114639:ReceiverDLQ
여기서 ARN을 기존 SQS 대기열에 지정합니다.
불행하게도 현재는 실패하고 오류가 발생했습니다.
onError currently only supports SNS topic arns due to a race condition when using SQS queue arns and updating the IAM role. Please check the docs for more info.
the doc에서도 이 점을 언급했다.
다행히도 onError 속성을 사용하지 않고 오래된 좋은 CloudFormation 스타일로 설정하는 해결 방법이 있습니다.
먼저 배포 중에 서버가 없는 SQS 대기열을 만들려면 resources 섹션의 serverless.yaml 에서 새 SQS 대기열을 정의해야 합니다.
resources:
  Resources:
    ReceiverDeadLetterQueue:
      Type: "AWS::SQS::Queue"
      Properties:
        QueueName: "receiverDLQ"
        MessageRetentionPeriod: 1209600 # 14 days in seconds
그리고 오류 처리 설정을 추가해야 합니다.여기에는 두 가지 선택이 있습니다.
  • onLambda 함수
  • 를 사용하여 DLQ 구성
  • 소스 대기열에 구성DeadLetterConfig.
  • 나는 첫 번째 선택을 시도했지만 성공하지 못했다.나의 시험 항목이 실패했고, 모든 해석을 합쳐도 Github 있다.
    여기서, 나는 두 번째 옵션을 설명할 것이다. 그것은 잘 작동하고 있다.
    먼저 새 응용 프로그램 스택을 만들 때 생성할 리소스를 정의합니다.
    
    resources:
      Resources:
        MyQueue:
          Type: "AWS::SQS::Queue"
          Properties:
            QueueName: "MyQueue"
            VisibilityTimeout: 30
            MessageRetentionPeriod: 60
            RedrivePolicy:
              deadLetterTargetArn:
                "Fn::GetAtt":
                  - ReceiverDeadLetterQueue
                  - Arn
              maxReceiveCount: 1
        ReceiverDeadLetterQueue:
          Type: "AWS::SQS::Queue"
          Properties:
            QueueName: "receiverDLQ"
            MessageRetentionPeriod: 1209600 # 14 days in seconds
    
    
    
    우리는 두 개의 대기열을 정의했다. RedrivePolicyMyQueue, 그리고 원본 대기열ReceiverDeadLetterQueue에 재구동 정책을 설정했다.
    이렇게 하면 메시지가 원본 대기열에 의해 MyQueue 회 이상 수신되면 maxReceiveCount 로 리디렉션됩니다.
    중요한 것은 AWS 콘솔에서 ReceiverDeadLetterQueue 의 메시지를 보려면'메시지 수신'으로 간주되기 때문에 람다 함수는 뷰어 화면을 닫은 후 DLQ로 직접 전송되기 때문에 얻을 기회가 없다는 것을 명심하는 것입니다.
    이제 AWS에 애플리케이션 스택을 배치할 때 테스트를 수행할 수 있습니다.
    소스 코드는 브랜치MyQueue에서 찾을 수 있습니다here.
    먼저 redrive-policy 에 JSON 메시지를 제출하고 로그를 확인하여 함수가 예상대로 작동하는지 테스트할 수 있습니다.
    export QUEUE_URL=`aws sqs get-queue-url --queue-name MyQueue --query 'QueueUrl' --output text --profile=sls`
    aws sqs send-message --queue-url ${QUEUE_URL} --message-body "{ \"text\" : \"test2\"}" --profile=sls
    sls logs -f receiver -t
    
    다음과 같이 기록해야 합니다.
    2018-07-19 20:40:19.789 (+02:00)    b0a7c784-eeea-5901-8786-944e33b92c18    event:  {"Records":[{"messageId":"3a0a90f5-8fb5-4cbb-8b46-017039272637","receiptHandle":"AQEBfT4rwo5KGoqL1RGBVp4WlsSumH5ToYEENfhDGPXUv2RPdLNDsUTJ/3QjeevDqK5YPrQ1xoSixvACIYlMD7HtXNh0iRRS8VBqutR7tx8ZMkucagRweZ3WyYxXImrVcsD33NIPFlUeldQjRTTnPKfjo+BCZ5TZCs1ndnSKVrqwMzMi7MwTDtI4a1b0IxEJdhiWot7eJ7EwaPmdfVpgK6K/vUQeW+jep3M1cUhNGwoz7H/2bL2FVX54uzfjoShLY7JymmkAuPzqQ/3KQPPvOyMvuragXmzE8VxA+phHHl66hWBj0nlOSWwYbXAFI765/Ik6zPk+nwKm2PoQs3hvPl1aDzAU1FHtqYqaH6GmJ7AAaHwNnufv83ez2hoZGv2AhhDE","body":"{ \"text\" : \"test2\"}","attributes":{"ApproximateReceiveCount":"1","SentTimestamp":"1532025619465","SenderId":"AIDAJLGFXMWT34E5GOTAS","ApproximateFirstReceiveTimestamp":"1532025619496"},"messageAttributes":{},"md5OfBody":"9be3787fa6959a8ed52b3fd5be1aa95a","eventSource":"aws:sqs","eventSourceARN":"arn:aws:sqs:us-east-1:811338114639:MyQueue","awsRegion":"us-east-1"}]}
    2018-07-19 20:40:19.790 (+02:00)    b0a7c784-eeea-5901-8786-944e33b92c18    text:  test2
    
    
    그리고 오류가 있는 JSON 본문을 보내서 Lambda에서 오류를 일으킵니다. 예를 들어
    aws sqs send-message --queue-url ${QUEUE_URL} --message-body "test" --profile=sls
    
    애플리케이션 로그 보기:
    sls logs -f receiver -t
    
    너는 이런 잘못을 보게 될 것이다.
    SyntaxError: Unexpected token e in JSON at position 1
        at Object.parse (native)
        at exports.handler (/var/task/receiver.js:15:29)
    
    Lambda가 성공하지 못했기 때문에 메시지는 MyQueue로 되돌아갔지만, 두 번째입니다. (첫 번째는 당신이 그것을 보낼 때) DLQ로 다시 지정해야 합니다.
    DLQ에 메시지를 표시하고 새 메시지가 표시되는 것을 볼 수 있습니다.
    export DLQ_QUEUE_URL=`aws sqs get-queue-url --queue-name receiverDLQ --query 'QueueUrl' --output text --profile=sls`
    aws sqs receive-message --queue-url ${DLQ_QUEUE_URL} --visibility-timeout 0 --profile=sls
    
    참고: MyQueue 을 0으로 설정하면 명령을 실행할 때마다 같은 메시지를 조회할 수 있으며 메시지가 다시 보일 때까지 기다릴 필요가 없습니다.
    이제 DLQ의 실패 메시지에 대해 원하는 작업을 수행할 수 있습니다. 예를 들어 DLQ를 처리하고 다시 시도하여 주 응용 프로그램 대기열로 보낼지 삭제할지 결정할 수 있습니다.

    Lambda 함수의 오류 처리


    DLQ를 설정하지 않고 오류를 처리하기를 원한다고 생각해 보십시오.
    함수는 어떻게 보입니까?
    다음은 예입니다.
    // receiverErrorHandling.js
    
    'use strict';
    
    var AWS = require('aws-sdk');
    var sqs = new AWS.SQS({
        region: 'us-east-1'
    });
    
    
    exports.handler = (event, context, callback) => {
    
        const NUM_OF_RETRIES = 3;
        try {
            console.log('event: ', JSON.stringify(event));
    
            throw new Error("simulated error");
    
            // this will never be reached in our demo
            callback(null, "How did you get here??");
    
        } catch (e) {
            console.log('Handled error', e);
    
            // we will send new message with incremented count, if below retry limit, otherwise exit with status code 200
            // to allow AWS to remove SQS message, but return status message.
            var message = JSON.parse(event.Records[0].body); // message boody arrives as string JSON
            var retried =  message.retryCount | 0; // we've set batchSize=1 in sls config so it's save to get by index.
            if (retried > NUM_OF_RETRIES-1) {
                const response = "Failed after retries";
                console.log(response);
                callback(null, response);
            } else {
                retried++;
                message.retryCount = retried;
    
                // send a new message which is a copy of received message but with incremender retry counter.
                var accountId = context.invokedFunctionArn.split(":")[4];
                var queueUrl = 'https://sqs.us-east-1.amazonaws.com/' + accountId + '/MyQueue';
    
                var params = {
                    MessageBody: JSON.stringify(message),
                    QueueUrl: queueUrl,
                    DelaySeconds: 10
                };
    
                sqs.sendMessage(params, function (err, data) {
                    if (err) {
                        console.log(err);
                        callback( "Failed to retry after error" );
                    } else {
                        const response =  "Failed, but will retry " + retried + " time";
                        console.log(response);
                        callback(null,response);
                    }
    
                });
            }
        }
    };
    
    이 함수는 다음과 같은 주요 오류를 처리합니다.
  • 오류가 --visibility-timeout 블록에 던져지고 잡히면 외부에서 전파하는 것을 허락하지 않습니다. 그렇지 않으면 메시지는 대기열에 남아 다시 Lambda에 도착합니다.
  • 블록에서 우리는 오류를 어떻게 처리하는지 결정해야 한다.이런 상황에서 Lambda에게 같은 메시지를 두 번 다시 시도하도록 하겠습니다.우리는 다시 시도하는 횟수를 포함하는 새로운 메시지를 대기열에 보내야 합니다.메시지에서, 우리는 이 함수를 즉시 호출하고 싶지 않기 때문에, MyQueue 속성을 설정했습니다.E, g. 만약 try 블록에 있다면, 우리는 일부 외부 서비스에 연결될 것이며, 이로 인해 오류가 발생할 것이다. 우리는 지연 시간이 지나면 그들이 스스로 복구하고 다시 시도할 것이라고 예상한다.
  • SQS를 일시적으로 사용할 수 없기 때문에 메시지를 보낼 수 없습니다.이 경우, Lambda 리셋에서 오류를 되돌려줍니다. 같은 메시지가 대기열로 되돌아옵니다.람다가 다시 처리할게요.만약 우리가 한 번 또 한 번 메시지를 보낼 수 없다면, Lambda는 메시지가 대기열에서 만료될 때까지 catch 에서 60초로 정의할 것입니다.
  • resources:
      Resources:
        MyQueue:
          Type: "AWS::SQS::Queue"
          Properties:
            QueueName: "MyQueue"
            VisibilityTimeout: 30 # 30 sec.
            MessageRetentionPeriod: 60 # 60 sec.
    
    메시지는 JSON을 보내더라도 해당 메시지를 대상으로 해석해야 하는 텍스트 형식입니다.
        var message = JSON.parse(event.Records[0].body);
    
    또한 Lambda는 여러 SQS 메시지를 동시에 처리할 수 있습니다. 첫 번째 메시지는 다음과 같습니다.
    event.Records[0]
    
    다른 상황에서는 안전하지 않을 수 있지만, Lambda는 다음과 같이 구성의 단일 메시지 DelaySeconds 만 폴링합니다.
      receiver:
        handler: receiverErrorHandling.handler
        events:
          - sqs:
              arn:
                Fn::GetAtt:
                  - MyQueue
                  - Arn
          - batchSize: 1
    
    Github에서 코드를 가져와 자신을 테스트할 수 있습니다.
    JSON 메시지를 보낼 수 있습니다.
    curl <sender url> -d '{"text" : "Hello"}'
    
    및 로그 보기:
    sls logs -f receiver -t
    
    다음 로그 시퀀스가 표시됩니다.
    START RequestId: 72ddf8e2-a835-5d10-9088-ab37291615f8 Version: $LATEST
    2018-07-27 22:22:10.532 (+02:00)    72ddf8e2-a835-5d10-9088-ab37291615f8    event:  {"Records":[{"messageId":"5d6caf38-23ff-4408-bba0-85b538fad092","receiptHandle":"AQEBV5+zoh9qYCqoKGye8D4gYjpXp1cXTUzm2gNLyAvpEV3XBRsDtyPn81C0G4LaMm3urF6yOUi8or6Zg29V+c7MjRq1nvw4XBk9rGEADdkVqd4/YGo2eGdfbuSOoPVQUbfN2Qk36VG/rjHmQHAYNTudfC4dD6upMfLHNsQEvaiUCUViF7ONa7H0ZGqcvEqNSfv6wH992PpRsRHkgJ52KhqVGVkn2wGUzO7djUv4zMfWWtD7ZGI0N6DB15OpeTtZTYtW8qbzcINuj51/B3Ty5oKh864sidQddm4VuqQQkOEOKVu4n+j/fAW+yOX20RfxJno+mCoh04gD7eEvGI/XgpAKAYsInKEpC8Meu6rjl9Icy2GFUp14e3Za1M8u9VlzsOZV","body":"{\"text\" : \"Hello\"}","attributes":{"ApproximateReceiveCount":"1","SentTimestamp":"1532722929910","SenderId":"AROAJ77G56ARJABY7JWOY:sqs-triggers-error-handling-dev-sender","ApproximateFirstReceiveTimestamp":"1532722929911"},"messageAttributes":{},"md5OfBody":"35efbdc1c909ad760d1b85d56e450139","eventSource":"aws:sqs","eventSourceARN":"arn:aws:sqs:us-east-1:811338114639:MyQueue","awsRegion":"us-east-1"}]}
    2018-07-27 22:22:10.532 (+02:00)    72ddf8e2-a835-5d10-9088-ab37291615f8    Handled error Error: simulated error
        at exports.handler.e (/var/task/receiverErrorHandling.js:17:9)
    2018-07-27 22:22:10.702 (+02:00)    72ddf8e2-a835-5d10-9088-ab37291615f8    Failed, but will retry 1 time
    END RequestId: 72ddf8e2-a835-5d10-9088-ab37291615f8
    REPORT RequestId: 72ddf8e2-a835-5d10-9088-ab37291615f8  Duration: 174.19 ms Billed Duration: 200 ms     Memory Size: 1024 MB    Max Memory Used: 34 MB  
    ...
    2018-07-27 22:22:20.768 (+02:00)    08200246-1f91-5f43-983f-51939ea57388    Failed, but will retry 2 time
    ...
    2018-07-27 22:22:30.830 (+02:00)    4313584b-9c88-5da8-a5ac-0b9e1db69a3a    Failed, but will retry 3 time
    ...
    2018-07-27 22:22:40.876 (+02:00)    7c1ab10d-66ab-58d1-ac12-e2d8e7ed3f43    Failed after retries
    ...
    
    로그에서 10초마다 다시 시도하는 시간 스탬프를 볼 수 있습니다.이것이 바로 우리가 SQS에 보내는 메시지에 설정try을 통해 실현하고자 하는 것이다.

    배달:

  • SQS 이벤트 처리가 까다로울 수 있습니다.메시지 지연, 가시성 시간, 보존 시간, 처리된 대량 크기
  • 와 같은 설정을 기억해야 합니다.
  • Lambda의 오류 처리는 여러 가지 방식으로 이루어질 수 있으며, tr-catch로 수동 처리할 수도 있고, 사신 대기열 또는 양자의 혼합
  • 을 사용할 수도 있다.
  • 사신 대기열을 사용하면 오류 처리 논리와 응용 프로그램 논리를 결합시킬 수 있습니다.try-catch의 두 번째 예는 약간 혼란스러웠습니다:)
  • 참고 문헌:

  • Using AWS Lambda with Amazon SQS
  • SQS docs
  • Lambda retries on erros
  • Amazon SQS Delay
  • 좋은 웹페이지 즐겨찾기