Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#70 SQS Message Produce 실패에 대한 처리 구현 #71

Merged
merged 2 commits into from
Sep 21, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lionheart-api/build.gradle
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ dependencies {

// sqs
implementation "org.springframework.cloud:spring-cloud-aws-messaging:2.2.6.RELEASE"
implementation 'org.springframework.retry:spring-retry:1.3.4'


// // s3
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.chiwawa.lionheart.api.config.sqs;

import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SqsRetryListener implements RetryListener {
@Override
public <T, E extends Throwable> boolean open(RetryContext retryContext, RetryCallback<T, E> retryCallback) {
log.info("SqsRetryListener Opened");
return true;
}

@Override
public <T, E extends Throwable> void close(RetryContext retryContext, RetryCallback<T, E> retryCallback,
Throwable throwable) {
log.info("SqsRetryListener Closed");
}

@Override
public <T, E extends Throwable> void onError(RetryContext retryContext, RetryCallback<T, E> retryCallback,
Throwable throwable) {
int retryCount = retryContext.getRetryCount();
log.info("Sqs Message Produce 과정에서 에러가 발생하였습니다. :: (RetryCount: {})", retryCount, throwable);
}
}
Original file line number Diff line number Diff line change
@@ -1,57 +1,73 @@
package com.chiwawa.lionheart.api.config.sqs.producer;

import java.util.Map;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import static com.chiwawa.lionheart.common.exception.ErrorCode.SQS_EXCEPTION;

import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.model.AmazonSQSException;
import com.amazonaws.services.sqs.model.MessageAttributeValue;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import com.chiwawa.lionheart.common.constant.MessageType;
import com.chiwawa.lionheart.common.dto.sqs.MessageDto;
import com.chiwawa.lionheart.common.exception.model.InternalServerException;
import com.chiwawa.lionheart.common.util.MessageUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Map;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SqsProducer {

@Value("${cloud.aws.sqs.notification.url}")
private String notificationUrl;

private static final String messageGroupId = "sqs";
private final ObjectMapper objectMapper;
private final AmazonSQS amazonSqs;
private static final String SQS_QUEUE_REQUEST_LOG_MESSAGE = "====> [SQS Queue Request] : %s ";

public SqsProducer(ObjectMapper objectMapper, AmazonSQS amazonSqs) {
this.objectMapper = objectMapper;
this.amazonSqs = amazonSqs;
}

public void produce(MessageDto dto) {
try {
SendMessageRequest sendMessageRequest = new SendMessageRequest(notificationUrl,
objectMapper.writeValueAsString(dto))
.withMessageGroupId(messageGroupId)
.withMessageDeduplicationId(UUID.randomUUID().toString())
.withMessageAttributes(createMessageAttributes(dto.getType()));
amazonSqs.sendMessage(sendMessageRequest);
log.info(MessageUtils.generate(SQS_QUEUE_REQUEST_LOG_MESSAGE, dto));
} catch (JsonProcessingException exception) {
log.error(exception.getMessage(), exception);
}
}

private Map<String, MessageAttributeValue> createMessageAttributes(String type) {
final String dataType = "String";
return Map.of(MessageType.MESSAGE_TYPE_HEADER, new MessageAttributeValue()
.withDataType(dataType)
.withStringValue(type));
}
@Value("${cloud.aws.sqs.notification.url}")
private String notificationUrl;

private static final String messageGroupId = "sqs";
private final ObjectMapper objectMapper;
private final AmazonSQS amazonSqs;
private static final String SQS_QUEUE_REQUEST_LOG_MESSAGE = "====> [SQS Queue Request] : %s ";

public SqsProducer(ObjectMapper objectMapper, AmazonSQS amazonSqs) {
this.objectMapper = objectMapper;
this.amazonSqs = amazonSqs;
}

@Retryable(
maxAttempts = 3,
backoff = @Backoff(delay = 1000),
include = {AmazonSQSException.class},
listeners = {"SqsRetryListener"})
public void produce(MessageDto dto) {
try {
SendMessageRequest sendMessageRequest = new SendMessageRequest(notificationUrl,
objectMapper.writeValueAsString(dto))
.withMessageGroupId(messageGroupId)
.withMessageDeduplicationId(UUID.randomUUID().toString())
.withMessageAttributes(createMessageAttributes(dto.getType()));
amazonSqs.sendMessage(sendMessageRequest);
log.info(MessageUtils.generate(SQS_QUEUE_REQUEST_LOG_MESSAGE, dto));
} catch (JsonProcessingException exception) {
throw new AmazonSQSException("Message sending failed by json processing.");
}
}

@Recover
private void recoverListener(AmazonSQSException exception, MessageDto dto) {
throw new InternalServerException(
String.format("SQS로 메시지를 Produce 하는 과정에서 에러가 발생하며 메시지 발행에 실패하였습니다. :: Message -> %s", dto.toString())
, SQS_EXCEPTION);
}

private Map<String, MessageAttributeValue> createMessageAttributes(String type) {
final String dataType = "String";
return Map.of(MessageType.MESSAGE_TYPE_HEADER, new MessageAttributeValue()
.withDataType(dataType)
.withStringValue(type));
}
}
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ public enum ErrorCode {

// Internal Server Exception
INTERNAL_SERVER_EXCEPTION("I001", "서버 내부에서 에러가 발생하였습니다."),
SQS_EXCEPTION("I002", "SQS 연동 과정에서 서버 내부 에러가 발생하였습니다."),

// Bad Gateway Exception
BAD_GATEWAY_EXCEPTION("B001", "외부 연동 중 에러가 발생하였습니다.");
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package com.chiwawa.lionheart.domain.domain.common;

import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;

import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Getter;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)