on
AWS SQS
AWS SQS에 대해 정리합니다. 관련 아티클을 번역해 정리에 참고했습니다.
- Message Brokers
- Apache Kafka
- RabbitMQ
- AWS SQS/SNS
- 메시지 브로커를 선택할 때의 고려사항
- AWS SQS 사용해보기
- 1. IAM 설정
- 2. AWS에 SQS Queue 생성하기
- 3. 의존성 추가, 기본 설정
- 4. 메시지 발신
- 5. 메시지 수신
Message Queue란?
메시지 큐 소프트웨어는 비동기 통신을 통해 원격으로 상호작용할 수 있도록 합니다. 이런 통신 시스템은 메시지 전송을 순차적으로 처리하는데 메시지 큐는 전송하는 응용 프로그램의 필요에 따라 응답, 요청, 또는 알람으로 구성되는 각 어플리케이션으로 유지됩니다. 메시지 소프트웨어의 장점으로는 큐를 통해 시스템이 메시지의 데이터를 분석하는데 걸리는 시간과 관계없이 시스템이 동작할 수 있기 때문에 송신자는 수신자의 응답을 기다리지 않고도 작업을 계속할 수 있다는 점이 있습니다. 단순히 비동기식 통신을 용이하는 것 외에도, 메시지 큐는 메시지를 저장, 전송 및 대기열에서 삭제하는 역할도 수행합니다. 또한 필요한 경우 메시지 정보를 문서화해서 저장할 수도 있습니다.
Message Brokers - Kafka vs RabbitMQ vs AWS SNS/SQS
MSA는 메시징 및 비동기 통신에 크게 의존합니다. 서로 통신해야 하는 서비스를 개발할 때 가장 먼저 해야 할 중요한 선택 중 하나는 적절한 메세지 브로커를 선택하는 것입니다.
Apache Kafka
Kafka는 아파치 소프트웨어 재단이 개발 및 관리하는 오픈 소스 메시지 브로커입니다.
주요 기능
- 대용량 데이터 스트림으로 작업하면서 스트리밍 가능한 컨텐츠에 집중합니다. 다른 메시지 대기열보다 실시간 데이터 파이프라인에서 선호되는 경우가 많습니다.
- 전반적으로 데이터 스트리밍, 메시지 브로커 시스템, 로그 애그리게이터 등 많은 서비스를 지원할 수 있는 다용도 플랫폼입니다.
- 메세지 지속성과 재처리 가능성이 핵심 기능입니다. 파티션을 사용하여 topic을 병렬화할 수 있으며 각 파티션은 서로 다른 third-party에서 호스팅될 수 있습니다.
- Kafka는 consumer 간 통신을 주도하는 pub/sub 모델을 통해 최적화된 스트림 기반 이벤트 처리를 제공합니다.
- 이러한 이벤트들은 분산된 응용 프로그램의 통신 패턴을 더 잘 구성할 수 있도록 topic으로 세분화될 수 있습니다.
- 또한 복원력과 성능이 뛰어난 메세지 전송 시스템을 제공할 수 있도록 클러스터 내의 여러 서버로 분할됩니다.
- Kafka는 독립 실행 서버, 가상머신이나 도커 컨테이너 등을 활용해 자체 어플리케이션의 아키텍처 내에서 배포될 수 있도록 설계되어 있습니다.
강점과 약점
- Kafka는 성능 통계에서 볼 수 있듯 데이터 스트림 처리량에 중점을 두고 있습니다.
- 이렇게 데이터 스트림을 처리하는데 초점을 맞추면 처리량이 높은 시스템이 생성되므로 대규모 데이터 스트림을 복잡하게 처리할 수 있습니다.
- 반면, 데이터 스트림에 대한 Kafka의 라우팅 기능은 다른 메시지 브로커에 비해 상대적으로 제한적입니다.(물론 그 차이는 제품 개선을 통해 줄어들고 있습니다.)
- 요약하자면, Kafka는 강력하고 내결함성이 뛰어난 고성능 메시지 스트리밍을 제공할 수 있는 강력한 솔루션이며, 대역폭과 리소스에 따라 적절히 호스팅을 추상화할 수 있는, 트래픽 변동에 적절한 메시지 브로커입니다.
RabbitMQ
RabbitMQ는 Rabbit Technologies가 개발한 후 VMWare의 소유가 되었습니다.
주요 기능
- 대용량 데이터 스트림을 지원하는 메시징 기반 통신에 집중합니다.
- pub-sub, point-to-point 및 request-reply 메시징을 포함한 여러 메시징 기술이 지원되며 binary 형식으로 메시지 오브젝트를 큐로 전송하며 메시지를 동적으로 생성하거나 폐기할 수 있습니다.
- 동기식 및 비동기식 통신 모드를 모두 사용할 수 있습니다.
- 클러스터의 여러 노드에 걸쳐 큐를 복제하여 하드웨어 장애가 발생하더라도 메시지가 손실되지 않도록 함으로써 고가용성이 보장됩니다.
- 복잡한 라우팅 기능을 핵심 기능으로 제공합니다.
- 다른 third-party의 호스팅을 지원합니다.
강점과 약점
- RabbitMQ는 거의 모든 크기의 workload를 처리할 수 있는 기능을 갖추고 있으며, 사용자 기반이 증가함에 따라 어플리케이션과 함께 효과적으로 확장할 수 있습니다.
- 메시지 보안이 중요한 응용 프로그램에서 자주 사용합니다.
- 메시징 기반 제공 및 복잡한 라우팅 시나리오에 초점을 맞춰 모든 어플리케이션 아키텍처에 매우 적합합니다. 메시지 제어 기능을 높이거나 메시지 큐 시스템을 기존 인프라에 통합하려는 경우 좋은 옵션일 수 있습니다.
- 기존에는 데이터 스트림 처리에 큰 지원이 없었고 메시지들은 보통 스트림을 재처리할 수 없는 상태에서 메시지가 한번만 처리되곤 했지만 이런 부분들은 RabbitMQ가 성장하면서 사라졌습니다.
- 신뢰성, 사용자 친화적이지만 다른 메시지 브로커만큼 확장성이 뛰어나지는 않고 중앙 노드의 미러링과 지연시간이 처리량을 감소시키곤 합니다.
Amazon Web Services SQS/SNS
SNS는 여러 클라이언트(ex. 모바일 디바이스, HTTPS 엔드포인트, 다른 AWS 서비스 등)에 메시지를 신속하게 배포할 수 있는 pub-sub 모델을 제공하여 메시지 전달에 중점을 두고 있습니다. 반면 SQS는 개별 클라이언트에 의한 성공적인 메시지 전달 및 처리에 초점을 맞추고 있습니다.
주요 기능
- SNS, SQS 모두 broadcast messaging과 pub/sub을 지원합니다.
- SNS의 경우 point-to-point, request-response messaging 용으로 설계되지는 않았습니다.
- AWS를 통해 빠르게 설정할 수 있습니다.
- AWS 밖에서는 호스팅할 수 없습니다.
- SNS는 동일한 메시지를 여러 수신자에게 브로드캐스트하는 반면 SQS는 단일 subscriber에게 큐 메시지를 전달합니다.
- SNS는 push-based 접근 방식으로 알림 응답을 사용해 알람에 대한 자동 응답을 허용하는 반면 SQS는 일부 추가적인 event-driven 기능이 제공되는 polling-style 방식에 더 집중합니다.
강점과 약점
- AWS SQS와 SNS를 함께 사용하여 확장성이 높고 복원력이 뛰어난 분산 애플리케이션의 기반을 구축할 수 있습니다.
- 또한 다른 많은 AWS 서비스(ex. AWS Lambda)와 연계되어 있어 애플리케이션의 통신을 쉽게 확장하면서 서비스 상호 작용의 근본적인 복잡성을 관리하는 데 필요한 모든 툴을 제공할 수 있습니다.(사용하기 쉽고, 신뢰할 수 있음) 따라서 이미 웹 애플리케이션이 AWS에서 실행중이라면 굉장히 간단하게 설정할 수 있으나 잠재적으로 메시지 수가 증가하게 되면 AWS bill을 고려해야 합니다.
- Kafka와 RabbitMQ는 기본 메시지 크기 제한을 제공하지 않지만 AWS는 SQS와 SNS 메시지 크기에 몇 가지 제한이 있습니다. - 메시지가 특정 크기에 도달한 후에는 S3 개체로 변환됨
- Simple한 메시지 큐 서비스인만큼 전반적인 기능이 메시지에 대한 send/receive에만 집중되어 있고 AWS 클라우드 환경에서 메시지 복제를 통해 장애 대비에 최적화되어 있습니다.
정리
메시지 브로커를 선택할 때의 고려사항
- 전송하려는 메시지의 타입
- 메시지 브로커를 선택할 때는 발송하려는 메시지와 형식을 파악해야 합니다.
- 어플리케이션의 인프라 구조
- 만약 메시지 보존과 데이터 재처리 용이성이 중요하다면 Kafka가 가장 적합할 수 있습니다.
- 만약 복잡한 라우팅 규칙을 구현하고 유지하는 것이 중요하다면 RabbitMQ가 가장 적합할 수 있습니다.
- 만약 최소의 오버헤드로 신속하게 시작하고 실행하려면, 또 AWS만 어플리케이션을 구축하고 있다면 빠르게 설정할 수 있는 AWS SQS/SNS가 가장 적합할 수 있습니다.
Amazon SQS 사용해보기
해당 실습은 기본값으로 설정해 진행했습니다.
1. IAM 설정
- IAM을 생성하고
AmazonSQSFullAccess
권한을 부여하면 액세스키와 비밀키를 주입할 수 있습니다. - 만약 AWS SQS를 사용하는 어플리케이션이 EC2에 구동되고 있다면 코드에 credentials 관련 부분을 직접 넣지 않고 IAM role을 EC2에 부여해서 안전하게 사용할 수 있습니다.
2. AWS에 SQS Queue 생성하기
1) 세부 정보
2) 구성
- 표시 제한 시간: (한 consumer가) queue에서 수신한 메시지가 다른 메시지 consumer에게 보이지 않게 되는 시간입니다.
- 한 서버가 메시지를 수신한 후 해당 시간만큼은 다른 서버에게 메시지를 보내지 않습니다.
- 메시지 보존 기간: Amazon SQS가 삭제되지 않은 메시지를 보관하는 시간입니다.
- 메시지를 수신한 후 메시지 삭제 요청을 보내게 되는데 해당 삭제 요청이 오기 전까지는 보존 기간만큼의 시간 동안 메시지를 삭제하지 않습니다.
- 전송 지연: 이 queue에 추가된 각 메시지의 첫 번째 전송에 대한 지연 시간입니다.
- 큐에 추가된 메시지가 얼마동안 다른 곳에 전송되지 못하도록 할건지에 대한 설정입니다.
- 최대 메시지 크기: queue의 최대 메시지 크기입니다.
- 메시지 수신 대기 시간: 폴링이 메시지를 사용할 수 있을 때까지 기다리는 최대 시간입니다.
- 콘텐츠 기반 중복 제거: Amazon SQS는 메시지 본문에 기반하여 중복 제거 ID를 자동으로 생성할 수 있습니다.
3) 액세스 정책
4) 기타 - 선택 사항 (기본 - 비활성화)
- 리드라이브 허용 정책: 모든 소스 queue들이 이 queue를 DLQ로 사용하도록 허용하는 것과 동일한 동작이 발생합니다.
- 암호화: 활성화하면 권한 있는 consumer에게 전송되는 경우에만 메시지가 해독됩니다.
- 배달 못한 편지 대기열(DLQ): 메시지를 소비할 수 없는 경우 DLQ로 전송할 수 있습니다. 문제가 있는 메시지를 격리하여 실패 원인을 확인할 수 있습니다.(디버깅 시 유용합니다.)
3. 의존성 추가, 기본 설정
build.gradle
// https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-aws-messaging
implementation group: 'org.springframework.cloud', name: 'spring-cloud-aws-messaging', version: '2.2.6.RELEASE'
implementation group: 'org.springframework.cloud', name: 'spring-cloud-aws-autoconfigure', version: '2.2.6.RELEASE'
application
설정
cloud:
aws:
stack:
auto: false
region:
static: ap-northeast-2
credentials:
access-key: [ Access Key ]
secret-key: [ Secret Key ]
cloud.aws.stack.auto=false
cloud.aws.region.static=ap-northeast-2
cloud.aws.credentials.access-key=[Access Key]
cloud.aws.credentials.secret-key=[Secret Key]
AWS Credential 설정
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.config.SimpleMessageListenerContainerFactory;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
public class SqsMessageConfig {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
//AWS SQS에 연결해주는 클라이언트
//해당 클라이언트를 이용하여 메세지 리스너와 전송 클라이언트를 SQS에 연동할 수 있습니다.
@Primary
@Bean(destroyMethod = "shutdown")
public AmazonSQSAsync amazonSQSAws() {
// 1) BasicAWSCredentials에 필요한 credentials를 설정하는 부분
AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonSQSAsyncClientBuilder.standard()
// 1) ~ 3) 방법 중 하나를 골라서 사용할 수 있습니다.
// 설정을 아예 안 한다면? -> DefaultAWSCredentialsProviderChain에 의해 우선순위가 가장 높은 3) 방법이 적용됩니다.
// 1) BasicAWSCredentials -> 직접 코드에 넣어서 주입하는 방법
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
// 2) EnvironmentVariableCredentialsProvider -> 환경변수로 주입하는 방법
.withCredentials(new EnvironmentVariableCredentialsProvider())
// 3) InstanceProfileCredentialsProvider를 주입 후 ec2에 배포, IAM role을 어플리케이션 구동하는 ec2에 바로 부여
.withCredentials(InstanceProfileCredentialsProvider.getInstance())
.withRegion(Regions.AP_NORTHEAST_2)
.build();
}
//메시지 리스너
@Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSQSAsync) {
SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
factory.setAmazonSqs(amazonSQSAsync);
return factory;
}
//메시지 전송 클라이언트
@Primary
@Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs) {
return new QueueMessagingTemplate(amazonSqs, (ResourceIdResolver) null, new MappingJackson2MessageConverter());
}
}
4. 메시지 발신
@Service
public class SqsMessageSender {
private final QueueMessagingTemplate queueMessagingTemplate;
@Autowired
public SqsMessageSender(AmazonSQS amazonSQS) {
this.queueMessagingTemplate = new QueueMessagingTemplate((AmazonSQSAsync) amazonSQS);
}
public void send(String message) {
Message<String> sendMessage = MessageBuilder.withPayload(message).build();
queueMessagingTemplate.send("생성한 queue 이름", sendMessage);
// 객체를 전송 할 때는 queueMessagingTemplate.convertAndSend()을 사용해 MessageConverter 인터페이스에 직렬화 책임을 위임할 수 있습니다.
}
}
5. 메시지 수신
@Component
public class SqsMessageListener {
@SqsListener(value = "생성한 queue 이름", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void listen(@Payload String message, @Headers Map<String, String> headers) {
}
}
@Payload
- 발신한 메시지 내용을 담고 있습니다.
@Headers
- LogicalResourceId, ApproximateReceiveCount, SentTimestamp, ReceiptHandle, Visibility, SenderId, contentType, lookupDestination, ApproximateFirstReceiveTimestamp, MessageId 등의 정보를 담고 있습니다.
SqsMessageDeletionPolicy
메시지를 받은 이후의 삭제 정책으로 네 가지가 있습니다.
SqsMessageDeletionPolicy.ALWAYS
: 메시지를 받으면 무조건 삭제 요청을 보냅니다.SqsMessageDeletionPolicy.NEVER
: 삭제 요청을 보내지 않기 때문에 설정한 폴링 기간마다 계속해서 메시지를 받게 됩니다. Acknowledgement 파라미터를 바인딩 받아 ack 메시지를 호출할 때 삭제 요청을 보냅니다.SqsMessageDeletionPolicy.NO_REDRIVE
: DLQ가 설정되어있지 않으면 삭제 요청을 보냅니다.SqsMessageDeletionPolicy.ON_SUCCESS
:@SqsListener
가 붙은 메서드에 오류가 발생하지 않으면 삭제 요청을 보냅니다.
6. DLQ(Dead-Letter Queue)
- 메시지를 가져간 후 삭제에 대한 요청이 오지 않는 경우 메시지가 DLQ에 들어갑니다.
- 최대 수신 수: 어플리케이션에서 최대 몇 번 수신 가능하도록 할 지 설정할 수 있습니다.
- ex. 5 -> 다섯 번 메시지를 수신했는데 다섯 번 모두 처리 실패해 삭제 요청을 날리지 않은 경우 DLQ에 메시지가 저장됩니다.
- 삭제될 메시지가 들어갈 Queue를 생성합니다.
- Queue ARN에 기존에 생성한 Queue를 넣어줍니다.
- DLQ를 사용할 때 DLQ의 최대 보관 기간은 기존 Queue의 보관 기간보다 길어야 합니다.
참고:
Kafka vs RabbitMQ vs AWS SNS/SQS: Which Broker to Choose?
Kafka vs ActiveMQ vs RabbitMQ vs Amazon SNS vs Amazon SQS vs Google pub/sub