Amazon Web Services 한국 블로그

Amazon SQS DLQ(Dead Letter Queue) 리드 라이브를 위한 새로운 API 세트

오늘 Amazon Simple Queue Service (Amazon SQS)를 위한 새로운 API 세트를 출시했습니다. 이러한 새 API를 사용하면 프로그래밍 방식으로 DLQ(Dead Letter Queue) 리드라이브를 관리할 수 있습니다. 이제 AWS SDK 또는 AWS Command Line Interface(AWS CLI)를 사용하여 프로그래밍 방식으로 메시지를 DLQ에서 원래 대기열이나 사용자 지정 대기열 대상으로 이동하여 다시 처리를 시도할 수 있습니다. DLQ는 Amazon SQS가 소비자 애플리케이션에서 올바르게 처리되지 않은 메시지를 자동으로 이동하는 대기열입니다.

이 새로운 API가 어떻게 도움이 될 수 있는지 제대로 이해하기 위해 역사를 간단히 되돌아보도록 하겠습니다.

메시지 대기열은 현대적 애플리케이션 아키텍처의 필수적인 부분입니다. 개발자는 메시지 대기열을 통해 메시지 생산자와 소비자 간의 비동기 및 메시지 기반 통신을 허용하여 서비스를 분리할 수 있습니다. 대부분의 시스템에서 메시지는 소비자가 처리할 때까지 공유 저장소(대기열)에 보관됩니다. 메시지 대기열을 통해 개발자는 일시적인 서비스 장애에 대한 복원력이 있는 애플리케이션을 구축할 수 있습니다. 메시지 대기열은 메시지 처리의 우선순위를 지정하고 메시지를 처리하는 워커 노드 플릿의 규모를 조정하는 데 도움이 됩니다. 또한 이벤트 기반 아키텍처에서도 많이 사용됩니다.

비동기식 메시지 교환은 애플리케이션 아키텍처에서 생소한 개념이 아닙니다. 애플리케이션 간에 메시지를 비동기적으로 교환한다는 개념은 1960년대에 등장했으며 1972년 IBM이 OS/360용 TCAM을 출시하면서 처음 대중화되었습니다. 20년 후인 1993년 IBM MQ 시리즈(현재 IBM MQ)가 널리 도입되었고, 1998년 Sun Microsystems가 JMS(Java Messaging Service)를 출시하면서 Java 애플리케이션이 메시지 대기열과 상호 작용하는 표준 API가 되었습니다.

AWS는 2006년 7월 12일Amazon SQS를 출시했습니다. Amazon SQS는 확장성과 안정성, 복원력이 뛰어난 대기열 서비스로, ‘효과가 정확’합니다. 당시 Werner는 다음과 같은 글을 남겼습니다. “우리가 선택한 동시 실행 모델에서는 메시지 작업 프로세스가 해당 메시지에 대한 임대 잠금을 자동으로 획득합니다. 따라서 임대 기간이 만료되기 전에 메시지를 삭제하지 않으면 메시지를 다시 처리할 수 있게 되어 실패 처리가 매우 간단해집니다.

2014년 1월 29일에 DLQ(Dead Letter Queue)를 도입했습니다. DLQ를 사용하면 처리에 실패한 메시지가 대기열의 맨 위에 영원히 남아 대기열에 있는 다른 메시지가 처리되지 않을 수 있습니다. DLQ의 경우 각 대기열에는 처리할 메시지를 표시할 수 있는 횟수를 Amazon SQS에 알려주는 관련 속성(MaxReceiveCount)이 있습니다. 각 메시지에는 관련 수신 카운터(ReceiveCount)도 있습니다. 소비자 애플리케이션이 처리할 메시지를 선택할 때마다 메시지 수신 횟수가 1씩 증가합니다. ReceiveCountmaxReceiveCount보다 크면 Amazon SQS가 메시지를 지정된 DLQ로 이동하여 사람이 분석 및 디버깅할 수 있도록 합니다. 일반적으로 이러한 이벤트가 발생하면 경보를 DLQ와 연결하여 알림을 보냅니다. 메시지를 DLQ로 이동하는 일반적인 이유는 메시지 형식이 잘못되었거나 소비자 애플리케이션에 버그가 있거나 메시지를 처리하는 데 시간이 너무 오래 걸리기 때문입니다.

AWS re:Invent 2021에서 AWS는 Amazon SQS 콘솔의 DLQ(Dead Letter Queue) 리드라이브를 발표했습니다. 리드라이브는 실패한 메시지 수명 주기의 두 번째 부분을 해결합니다. 리드라이브를 통해 메시지를 원래 대기열에 다시 삽입하여 재처리를 시도할 수 있습니다. 소비자 애플리케이션이 수정되고 실패한 메시지를 사용할 준비가 되면 DLQ의 메시지를 소스 대기열이나 사용자 지정 대기열 대상으로 리드라이브할 수 있습니다. 콘솔을 몇 번 클릭하기만 하면 됩니다.

현재 AWS는 프로그래밍 방식으로 리드라이브를 처리하는 애플리케이션과 스크립트를 작성할 수 있는 API를 추가하고 있습니다. 더 이상 사람이 콘솔을 클릭하지 않아도 됩니다. API를 사용하면 프로세스의 확장성이 향상되고 작업자 오류의 위험이 줄어듭니다.

실제 작동 모습 살펴보기
이 새 API를 사용해 보기 위해 명령줄 전용 데모를 위한 터미널을 엽니다. 시작하기 전에 AWS CLI의 최신 버전이 설치되어 있는지 확인합니다. macOS에서 brew upgrade awscli를 입력합니다.

먼저 대기열을 두 개 만듭니다. 하나는 DLQ(Dead Letter Queue)이고 다른 하나는 제 애플리케이션 대기열입니다.

# 첫째, DLQ(Dead Letter Queue)를 만듭니다(대기열 이름 끝에 추가하기로 한 -dlq 확인).
➜ ~ aws sqs create-queue \
            --queue-name awsnewsblog-dlq                                            
{
    "QueueUrl": "https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog-dlq"
}

# 둘째, 방금 생성한 대기열의 Arn을 검색합니다.
➜  ~ aws sqs get-queue-attributes \
             --queue-url https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog-dlq \
             --attribute-names QueueArn
{
    "Attributes": {
        "QueueArn": "arn:aws:sqs:us-east-2:012345678900:awsnewsblog-dlq"
    }
}

# 셋째, 애플리케이션 대기열을 만듭니다. 리드라이브 정책을 입력합니다. 메시지 전송을 세 번 시도한 후 DLQ에 메시지를 게시합니다.
➜  ~ aws sqs create-queue \
             --queue-name awsnewsblog \
             --attributes '{"RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:us-east-2:012345678900:awsnewsblog-dlq\",\"maxReceiveCount\":\"3\"}"}' 
{
    "QueueUrl": "https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog"
}

이제 두 개의 대기열이 준비되었으므로 애플리케이션 대기열에 메시지를 게시합니다.

➜ ~ aws sqs send-message \
            --queue-url https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog \
            --message-body "Hello World"
{
"MD5OfMessageBody": "b10a8db164e0754105b7a99be72e3fe5",
"MessageId": "fdc26778-ce9a-4782-9e33-ae73877cfcb2"
}

다음으로 메시지를 소비하지만 대기열에서 삭제하지는 않습니다. 이는 메시지 소비자 애플리케이션의 충돌을 시뮬레이션합니다. 메시지 소비자는 성공적인 처리 후에 메시지를 삭제해야 합니다. RedrivePolicy를 입력할 때 MaxReceivedCount 속성을 3으로 설정했습니다. 따라서 이 작업을 세 번 반복하여 전송을 세 번 시도한 후 Amazon SQS가 메시지를 DLQ(Dead Letter Queue)로 이동하도록 합니다. 기본 가시성 제한 시간이 30초이므로 다음 재시도까지 30초를 기다려야 합니다.

➜ ~ aws sqs receive-message \
            --queue-url https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog
{
"Messages": [
{
"MessageId": "fdc26778-ce9a-4782-9e33-ae73877cfcb2",
"ReceiptHandle": "AQEBP8yOfgBlnjlkGXjyeLROiY7xg7cZ6Znq8Aoa0d3Ar4uvTLPrHZptNotNfKRK25xm+IU8ebD3kDwZ9lja6JYs/t1kBlwiNO6TBACN5srAb/WggQiAAkYl045Tx3CvsOypbJA3y8U+MyEOQRwIz6G85i7MnR8RgKTlhOzOZOVACXC4W8J9GADaQquFaS1wVeM9VDsOxds1hDZLL0j33PIAkIrG016LOQ4sAntH0DOlEKIWZjvZIQGdlRJS65PJu+I/Ka1UPHGiFt9f8m3SR+Y34/ttRWpQANlXQi5ByA47N8UfcpFXXB5L30cUmoDtKucPewsJNG2zRCteR0bQczMMAmOPujsKq70UGOT8X2gEv2LfhlY7+5n8z3yew8sdBjWhVSegrgj6Yzwoc4kXiMddMg==",
"MD5OfBody": "b10a8db164e0754105b7a99be72e3fe5",
"Body": "Hello World"
}
]
}

# 30초간 기다립니다.
# 그런 다음 두 번 반복합니다(총 3회 메시지 수신 API 호출의 경우). 

처리를 세 번 시도한 후에는 메시지가 더 이상 대기열에 없습니다.

➜  ~ aws sqs receive-message \
             --queue-url  https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog
{
    "Messages": []
}

메시지가 DLQ(Dead Letter Queue)로 이동했습니다. DLQ를 검사하여 확인합니다(대기열 URL이 -dlq로 끝나는 것을 확인).

➜  ~ aws sqs receive-message \
             --queue-url  https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog-dlq
{
    "Messages": [
        {
            "MessageId": "fdc26778-ce9a-4782-9e33-ae73877cfcb2",
            "ReceiptHandle": "AQEBCLtBMoZYVMMq7fUGNHeCliqE3mFXnkuJ+nOXLK1++uoXWBG31nDejCpxElmiBZWfbcfGJrEdKj4P9HJdrQMYDbeSqB+u1ZlB7CYzQBiQps4SEG0biEoubwqjQbmDZlPrmkFsnYgLD98D1XYWk/Ik6Z2n/wxDo9ko9rbZ15izK5RFnbwveNy8dfc6ireqVB1EGbeGkHcweHGuoeKWXEab1ynZWhNqZsQgCR6pWRkgtn59lJcLv4cJ4UMewNzvt7tMHH69GvVjXdYDYvJJI2vj+6RHvcvSHWWhTNT+CuPEXguVNuNrSya8gho1fCnKpVwQre6HhMlLPjY4wvn/tXY7+5rmte9eXagCqLQXaENB2R7qWNVPiWRIJy8/cTf37NLYVzBom030DNJlH9EeceRhCQ==",
            "MD5OfBody": "b10a8db164e0754105b7a99be72e3fe5",
            "Body": "Hello World"
        }
    ]
}

설정이 준비되었으므로 프로그래밍 방식으로 메시지를 원래 대기열로 리드라이브해 보겠습니다. 소비자가 메시지를 올바르게 처리하지 못한 이유를 이해하고 소비자 애플리케이션 코드를 수정했다고 가정해 보겠습니다. DLQ에서 start-message-move-task를 사용하여 비동기 리드라이브를 시작합니다. 리드라이브 속도를 제어하는 선택적 속성(MaxNumberOfMessagesPerSecond)이 있습니다.

➜ ~ aws sqs start-message-move-task \
            --source-arn arn:aws:sqs:us-east-2:012345678900:awsnewsblog-dlq
{
    "TaskHandle": "eyJ0YXNrSWQiOiI4ZGJmNjBiMy00MmUwLTQzYTYtYjg4Zi1iMTZjYWRjY2FkNmEiLCJzb3VyY2VBcm4iOiJhcm46YXdzOnNxczp1cy1lYXN0LTI6NDg2NjUyMDY2NjkzOmF3c25ld3NibG9nLWRscSJ9"
}

list-message-move-tasks로 시작한 이동 작업을 나열하고 작업의 상태를 확인하거나 cancel-message-move-task API를 호출하여 실행 중인 작업을 취소할 수 있습니다.

➜ ~ aws sqs list-message-move-tasks \
            --source-arn arn:aws:sqs:us-east-2:012345678900:awsnewsblog-dlq
{
    "Results": [
        {
            "Status": "COMPLETED",
            "SourceArn": "arn:aws:sqs:us-east-2:012345678900:awsnewsblog-dlq",
            "ApproximateNumberOfMessagesMoved": 1,
            "ApproximateNumberOfMessagesToMove": 1,
            "StartedTimestamp": 1684135792239
        }
    ]
}

이제 제 애플리케이션이 애플리케이션 대기열에서 메시지를 다시 사용할 수 있습니다.

➜  ~ aws sqs receive-message \
             --queue-url  https://sqs.us-east-2.amazonaws.com/012345678900/awsnewsblog                                   
{
    "Messages": [
        {
            "MessageId": "a7ae83ca-cde4-48bf-b822-3d4bc1f4dcae",
            "ReceiptHandle": "AQEB9a+Dm2nvb3VUn9+46j9UsDidU/W6qFwJtXtNWTyfoSDOKT7h73e6ctT9RVZysEw3qqzJOx1cxblTTOSrYwwwoBA2qoJMGsqsrsRGGYojBvf9X8hqi8B8MHn9rTm8diJ2wT2b7WC+TDrx3zIvUeiSEkP+EhqyYOvOs7Q9aETR+Uz02kQxZ/cUJWsN4MMSXBejwW+c5ivv5uQtpfUrfZuCWa9B9O67Kj/q52clriPHpcqCCfJwFBSZkGTXYwTpnjxD4QM7DPS+xVeVfTyM7DsKCAOtpvFBmX5m4UNKT6TROgCnGxTRglUSMWQp8ufVxXiaUyM1dwqxYekM9uX/RCb01gEyCZHas4jeNRV5nUJlhBkkqPlw3i6w9Uuc2y9nH0Df8nH3g7KTXo4lv5Bl3ayh9w==",
            "MD5OfBody": "b10a8db164e0754105b7a99be72e3fe5",
            "Body": "Hello World"
        }
    ]
}

정식 출시
오늘부터 Amazon SQS가 제공되는 모든 상용 리전에서 DLQ 리드라이브 API를 사용할 수 있습니다.

DLQ(Dead Letter Queue)에서 소스 대기열 또는 사용자 지정 대상 대기열로 메시지를 리드라이브하면 기존 요금에 따라 청구되는 추가 API 호출이 발생합니다(API 호출 100만 건당 0.40 USD부터 시작, 최초 100만 건 이후 매월 무료). Amazon SQS는 메시지를 한 대기열에서 다른 대기열로 리드라이브하면서 메시지를 일괄 처리합니다. 이렇게 하면 메시지를 한 대기열에서 다른 대기열로 간단하고 저렴하게 이동할 수 있습니다.

DLQ 및 DLQ 리드라이브에 대한 자세한 내용은 설명서를 참조하세요.

우리는 비동기식 세상에 살고 있다는 점을 기억하세요. 여러분의 애플리케이션도 그래야 합니다. 오늘 시작하여 첫 번째 리드라이브 애플리케이션을 작성하세요.

— seb