AWS SNS, SQS, SDK, Spring Cloud AWS를 사용해 애플리케이션 간 메시징 하기
업데이트:
MSA 환경에서 개발을 하다보면, Application 간에 메세지를 전달해야 할 일이 많다.
이 글에서는 AWS의 SNS, SQS, SDK, Spring Cloud AWS를 사용해 메세지를 전달하는 방법을 알아보겠다.
전체 코드는 여기에 있다.
이 글의 내용은 사내 AWS 놀이터 계정에서 진행했다.
EC2에 필요한 IAM 권한을 부여해 테스트하며 작성했다.
SNS, SQS, EC2간 메세지 통신을 하려면 EC2에 IAM 권한 설정이 필요할 것이다.
또한, 로컬에서 Spring Boot 애플리케이션을 띄워 테스트하려면 access key를 발급받아 사용해야 할 것이다.
이에 대한 내용은 나중에 추가하도록 하고, 지금은 따로 설명하지 않겠다.
필요한 분들은 따로 찾아보길 바란다.
AWS SNS 구성하기
먼저, AWS SNS를 구성해보자.
이 과정은 AWS 공식 문서를 참고했다.
AWS SNS 주제(Topic) 생성
Amazon SNS 콘솔
에 접속한다.
왼쪽 탐색 창에서 주제를 선택하고, 주제 페이지에서 주제 생성 버튼을 클릭한다.
SNS의 유형을 FIFO, 표준 중에 선택할 수 있다.
나는 FIFO를 선택했다.
SNS의 이름을 입력하고, 주제 생성 버튼을 누른다.
FIFO를 선택했을 경우, 이름 맨 뒤에는 반드시 “.fifo”를 붙여야 한다.
AWS SQS 구성하기
AWS SQS 콘솔을 열고, 대기열 생성 버튼을 누른다.
유형을 FIFO로 선택하고, SQS의 이름을 입력한다.
맨 아래 오른쪽의 대기열 생성 버튼을 누른다.
FIFO를 선택했을 경우, 이름 맨 뒤에는 반드시 “.fifo”를 붙여야 한다.
이후에 나오는 대기열 화면에서, SNS 구독 버튼을 누른다.
아까 생성해놓은 SNS를 검색해서 선택하고 저장 버튼을 누른다.
SNS로 다시 가보면, 구독 항목에 SQS가 추가되었음을 확인할 수 있다.
SNS -> SQS 메시지 게시
SNS의 주제에서 메시지 게시 버튼을 클릭한다.
위의 항목들을 채워넣은 뒤, 메시지 게시 버튼을 누른다.
-
메세지 그룹 ID : 동일한 그룹에 속하는 메시지는 엄격하게 순서대로 하나씩 처리된다. 다른 그룹에 속하는 메시지 사이의 순서는 다르게 처리될 수 있다.
메시지 그룹 ID는 메시지가 특정 메시지 그룹에 속하도록 지정하는 필수 토큰이다. 메시지 그룹 ID는 메시지를 SNS FIFO 주제에 게시할 때 설정한다.
-
메시지 중복 제거 ID : SNS FIFO에서 중복 제거는 주제가 엔드포인트에 도달할 수 있는 경우에 한해 구독된 엔드포인트에 메시지가 정확히 1회 전송됨을 의미한다.
메시지 중복 제거 ID는 메시지 중복 제거에 사용되는 토큰이다. 중복 제거 ID는 메시지를 SNS FIFO 주제에 게시할 때 설정한다. 주제에 콘텐츠 기반 중복 제거를 활성화하면 메시지 중복 제거 ID는 선택 사항이며 그 외의 경우 필수라는 점에 유의해야 한다. 콘텐츠 기반이라는 것은 메시지 내용을 기반으로 중복 여부를 판단한다는 의미이다. 즉, 메시지 내용이 동일하면 메시지 중복 제거 ID가 동일한것과 같은 효과가 발생한다.
SQS의 메시지 전송 및 수신을 클릭해보면,
위와 같이 사용 가능한 메시지가 1이 되었음을 확인할 수 있다.
메시지 폴링을 누르면, 아래에 SQS에 저장된 메시지들이 나열된다.
메시지의 ID를 누르면,
위와 같이 수신된 메시지의 제목과 내용을 볼 수 있다.
확인한 메세지는 삭제 버튼을 눌러 삭제해주자.
Application 구성
AWS SNS에 Topic을 publish하는 Application (from-application) 만들기
build.gradle 파일의 dependencies에
// https://mvnrepository.com/artifact/io.awspring.cloud/spring-cloud-aws-messaging
implementation 'io.awspring.cloud:spring-cloud-aws-messaging:2.4.0'
위의 종속성을 추가한다.
PublishSnsTopicRequest
@Getter
public class PublishSnsTopicRequest {
private String topicArn;
private String message;
private String messageGroupId;
private String messageDeduplicationId;
}
PublishSnsTopicController
@RequiredArgsConstructor
@RestController
public class PublishSnsTopicController {
private final PublishSnsTopicService publishSnsTopicService;
@PostMapping("/api/v1/sns/publish-topic")
public String publish(@RequestBody PublishSnsTopicRequest publishSnsTopicRequest) {
publishSnsTopicService.publish(publishSnsTopicRequest);
return "SNS Topic publish 성공!!";
}
}
PublishSnsTopicService
@Slf4j
@Service
public class PublishSnsTopicService {
public void publish(PublishSnsTopicRequest publishSnsTopicRequest) {
AmazonSNS amazonSNS = getAmazonSns();
try {
PublishRequest request = getPublishRequestBy(publishSnsTopicRequest);
amazonSNS.publish(request);
} catch (AmazonSNSException e) {
log.error(e.getMessage());
System.exit(1);
}
log.info("메세지 전송됨. publishSnsTopicRequest = {}", publishSnsTopicRequest);
}
public AmazonSNS getAmazonSns() {
return AmazonSNSClientBuilder
.standard()
.withRegion(Regions.AP_NORTHEAST_2)
.build();
}
private PublishRequest getPublishRequestBy(PublishSnsTopicRequest publishSnsTopicRequest) {
if (publishSnsTopicRequest.getFifo()) {
return new PublishRequest()
.withTopicArn(publishSnsTopicRequest.getTopicArn())
.withMessage(publishSnsTopicRequest.getMessage())
.withMessageGroupId(publishSnsTopicRequest.getMessageGroupId())
.withMessageDeduplicationId(publishSnsTopicRequest.getMessageDeduplicationId());
}
return new PublishRequest()
.withTopicArn(publishSnsTopicRequest.getTopicArn())
.withMessage(publishSnsTopicRequest.getMessage());
}
}
AWS SQS로부터 Message를 receive하는 Application (to-application) 만들기
build.gradle 파일에 다음을 추가한다.
...
ext {
set('springCloudVersion', 'Hoxton.SR10')
}
...
dependencies {
...
implementation 'org.springframework.cloud:spring-cloud-starter-aws-messaging'
...
}
...
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
전체 build.gradle 파일은 다음과 같다.
plugins {
id 'org.springframework.boot' version '2.6.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', 'Hoxton.SR10')
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-aws-messaging'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
SqsMessageListener
@Slf4j
@Component
public class SqsMessageListener {
@SqsListener(value = "${메세지를 수신할 SQS 이름}", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void receive(String message) {
log.info("수신한 메시지 : {}", message);
}
}
deletionPolicy
를 SqsMessageDeletionPolicy.ON_SUCCESS
로 설정하면,
메세지를 에러 없이 정상적으로 수신했을 때 SQS에서 수신한 메세지를 삭제한다.
위처럼 구성해서 from-application
과 to-application
을 각각 다른 EC2에 띄운다.
Postman을 사용해서 from-application
에 메세지를 publish 해 보면,
위와 같이 보내진다.
to-application
의 로그를 보면,
위와 같이 보냈던 메시지가 자동으로 잘 수신되는것을 확인할 수 있다.
AWS SDK
를 사용하면 수신을 수동으로 해줘야 하는데,
Spring Cloud AWS
를 사용하니, SNS에 publish를 하자마자 to-application
에서 자동으로 수신을 했고, 로그가 찍혔다!
그리고 from-application
에서 맨 마지막 세 개의 전송 요청의 messageDeduplicationId
가 모두 같은 것을 볼 수 있다.
그래서 to-application
에서 맨 마지막 두 개의 메세지는 수신되지 않은 것을 확인할 수 있다.
참고
- Amazon Simple Notification Service
- Spring Cloud AWS
- jojoldu/springboot-sqs-example
- Spring Cloud Messaging - SQS(Simple Queue Service) 사용해보기
- AWS SQS 들이파기
댓글남기기