7.7 KiB
7.7 KiB
Spring Boot Integration Reference
Configuration
Basic Bean Configuration
@Configuration
public class MessagingConfiguration {
@Bean
public SqsClient sqsClient() {
return SqsClient.builder()
.region(Region.US_EAST_1)
.build();
}
@Bean
public SnsClient snsClient() {
return SnsClient.builder()
.region(Region.US_EAST_1)
.build();
}
}
Configuration Properties
# application.yml
aws:
sqs:
queue-url: https://sqs.us-east-1.amazonaws.com/123456789012/my-queue
sns:
topic-arn: arn:aws:sns:us-east-1:123456789012:my-topic
Service Layer Integration
SQS Message Service
@Service
@RequiredArgsConstructor
public class SqsMessageService {
private final SqsClient sqsClient;
private final ObjectMapper objectMapper;
@Value("${aws.sqs.queue-url}")
private String queueUrl;
public <T> void sendMessage(T message) {
try {
String jsonMessage = objectMapper.writeValueAsString(message);
SendMessageRequest request = SendMessageRequest.builder()
.queueUrl(queueUrl)
.messageBody(jsonMessage)
.build();
sqsClient.sendMessage(request);
} catch (Exception e) {
throw new RuntimeException("Failed to send SQS message", e);
}
}
public <T> List<T> receiveMessages(Class<T> messageType) {
ReceiveMessageRequest request = ReceiveMessageRequest.builder()
.queueUrl(queueUrl)
.maxNumberOfMessages(10)
.waitTimeSeconds(20)
.build();
ReceiveMessageResponse response = sqsClient.receiveMessage(request);
return response.messages().stream()
.map(msg -> {
try {
return objectMapper.readValue(msg.body(), messageType);
} catch (Exception e) {
throw new RuntimeException("Failed to parse message", e);
}
})
.collect(Collectors.toList());
}
public void deleteMessage(String receiptHandle) {
DeleteMessageRequest request = DeleteMessageRequest.builder()
.queueUrl(queueUrl)
.receiptHandle(receiptHandle)
.build();
sqsClient.deleteMessage(request);
}
}
SNS Notification Service
@Service
@RequiredArgsConstructor
public class SnsNotificationService {
private final SnsClient snsClient;
private final ObjectMapper objectMapper;
@Value("${aws.sns.topic-arn}")
private String topicArn;
public void publishNotification(String subject, Object message) {
try {
String jsonMessage = objectMapper.writeValueAsString(message);
PublishRequest request = PublishRequest.builder()
.topicArn(topicArn)
.subject(subject)
.message(jsonMessage)
.build();
snsClient.publish(request);
} catch (Exception e) {
throw new RuntimeException("Failed to publish SNS notification", e);
}
}
}
Message Listener Pattern
Scheduled Polling
@Service
@RequiredArgsConstructor
public class SqsMessageListener {
private final SqsClient sqsClient;
private final ObjectMapper objectMapper;
@Value("${aws.sqs.queue-url}")
private String queueUrl;
@Scheduled(fixedDelay = 5000)
public void pollMessages() {
ReceiveMessageRequest request = ReceiveMessageRequest.builder()
.queueUrl(queueUrl)
.maxNumberOfMessages(10)
.waitTimeSeconds(20)
.build();
ReceiveMessageResponse response = sqsClient.receiveMessage(request);
response.messages().forEach(this::processMessage);
}
private void processMessage(Message message) {
try {
// Process message
System.out.println("Processing: " + message.body());
// Delete message after successful processing
deleteMessage(message.receiptHandle());
} catch (Exception e) {
// Handle error - message will become visible again
System.err.println("Failed to process message: " + e.getMessage());
}
}
private void deleteMessage(String receiptHandle) {
DeleteMessageRequest request = DeleteMessageRequest.builder()
.queueUrl(queueUrl)
.receiptHandle(receiptHandle)
.build();
sqsClient.deleteMessage(request);
}
}
Pub/Sub Pattern Integration
Configuration for Pub/Sub
@Configuration
@RequiredArgsConstructor
public class PubSubConfiguration {
private final SnsClient snsClient;
private final SqsClient sqsClient;
@Bean
@DependsOn("sqsClient")
public String setupPubSub() {
// Create SNS topic
String topicArn = snsClient.createTopic(CreateTopicRequest.builder()
.name("order-events")
.build()).topicArn();
// Create SQS queue
String queueUrl = sqsClient.createQueue(CreateQueueRequest.builder()
.queueName("order-processor")
.build()).queueUrl();
// Get queue ARN
String queueArn = sqsClient.getQueueAttributes(GetQueueAttributesRequest.builder()
.queueUrl(queueUrl)
.attributeNames(QueueAttributeName.QUEUE_ARN)
.build()).attributes().get(QueueAttributeName.QUEUE_ARN);
// Subscribe SQS to SNS
snsClient.subscribe(SubscribeRequest.builder()
.protocol("sqs")
.endpoint(queueArn)
.topicArn(topicArn)
.build());
return topicArn;
}
}
Error Handling Patterns
Retry Mechanism
@Service
@RequiredArgsConstructor
public class RetryableSqsService {
private final SqsClient sqsClient;
private final RetryTemplate retryTemplate;
public void sendMessageWithRetry(String queueUrl, String messageBody) {
retryTemplate.execute(context -> {
try {
SendMessageRequest request = SendMessageRequest.builder()
.queueUrl(queueUrl)
.messageBody(messageBody)
.build();
sqsClient.sendMessage(request);
return null;
} catch (Exception e) {
throw new RetryableException("Failed to send message", e);
}
});
}
}
Testing Integration
LocalStack Configuration
@TestConfiguration
public class LocalStackMessagingConfig {
@Container
static LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:3.0"))
.withServices(
LocalStackContainer.Service.SQS,
LocalStackContainer.Service.SNS
);
@Bean
public SqsClient sqsClient() {
return SqsClient.builder()
.region(Region.US_EAST_1)
.endpointOverride(
localstack.getEndpointOverride(LocalStackContainer.Service.SQS))
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey())))
.build();
}
@Bean
public SnsClient snsClient() {
return SnsClient.builder()
.region(Region.US_EAST_1)
.endpointOverride(
localstack.getEndpointOverride(LocalStackContainer.Service.SNS))
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey())))
.build();
}
}