Spring Cloud AWS FIFO Queue Support Example
Amazon Simple Queue Service (SQS) is a fully managed message queuing service. AWS provides two types of SQS queues: Standard and FIFO (First-In-First-Out). Let us delve into understanding Spring Cloud AWS FIFO queue support.
1. Understanding FIFO and AWS SQS
1.1 What is FIFO (First-In-First-Out)?
FIFO (First-In-First-Out) is a queue processing mechanism where messages are processed in the exact order in which they arrive. This ensures that no message overtakes another, maintaining strict order. Additionally, FIFO prevents duplicates, guaranteeing that each message is delivered and processed exactly once. This is particularly useful in scenarios where order consistency is critical, such as financial transactions, inventory management, and task scheduling.
1.2 What is AWS SQS?
Amazon Simple Queue Service (SQS) is a fully managed message queuing service that enables applications to communicate asynchronously by sending, storing, and receiving messages at scale. It helps decouple the components of distributed applications, improving their resilience and scalability.
AWS SQS provides two types of queues:
- Standard Queue – Offers high throughput, best-effort ordering, and at least-once delivery. Suitable for general-purpose applications where message order is not critical.
- FIFO Queue – Ensures messages are delivered in order and exactly once, making it ideal for applications requiring strict sequence guarantees.
With AWS SQS, developers can:
- Decouple microservices, distributed systems, and serverless applications.
- Handle high message volumes efficiently without managing infrastructure.
- Control message visibility, retention, and delay processing for better workflow management.
- Secure message transmission using AWS Identity and Access Management (IAM) policies and encryption.
1.3 What are TestContainers?
Testcontainers is an open-source library that enables developers to create lightweight, throwaway instances of databases, message brokers, and other services in Docker containers for integration testing. It simplifies the process of setting up and managing dependencies, ensuring consistent test environments. The key benefits of Testcontainers include improved test reliability, faster feedback loops, and easier setup of complex dependencies. It is widely used for testing microservices, database interactions, and cloud-native applications without requiring dedicated infrastructure. By leveraging Docker, Testcontainers provides a clean, isolated environment, reducing flakiness in tests and making continuous integration pipelines more robust.
2. Running LocalStack in a TestContainer
LocalStack is an open-source tool that provides a fully functional local AWS cloud environment, enabling developers to test and develop cloud applications without needing access to an actual AWS account. It simulates core AWS services such as S3, DynamoDB, Lambda, API Gateway, and more, allowing users to run their cloud applications entirely on their local machines. By leveraging LocalStack, developers can accelerate their development cycles, reduce costs associated with AWS usage, and ensure their applications work correctly before deploying them to a real AWS environment. It integrates seamlessly with CI/CD pipelines and supports both Docker-based and native installations, making it a versatile tool for cloud-native application development.
We use TestContainers to run LocalStack in an isolated environment. Ensure that Docker is installed on your machine before proceeding.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; public class LocalStackContainer { public static void main(String[] args) { // Create a GenericContainer instance for LocalStack GenericContainer<?> localStack = new GenericContainer<>(DockerImageName.parse( "localstack/localstack" )) // Expose port 4566, which is used for AWS service emulation in LocalStack .withExposedPorts( 4566 ) // Set environment variable to enable the SQS (Simple Queue Service) in LocalStack .withEnv( "SERVICES" , "sqs" ); // Start the LocalStack container localStack.start(); // Print the LocalStack host address to verify it's running System.out.println( "LocalStack running at: " + localStack.getHost()); } } |
2.1 Code Explanation
The LocalStackContainer
class initializes and starts a LocalStack container using the Testcontainers library. It defines a GenericContainer
instance, specifying the LocalStack Docker image (localstack/localstack
) using DockerImageName.parse
. The container is configured to expose port 4566
, which is the default entry point for AWS services in LocalStack. Additionally, the environment variable SERVICES
is set to sqs
, ensuring that only the Amazon Simple Queue Service (SQS) is enabled. The localStack.start()
method launches the container, making LocalStack available for local AWS service testing. Finally, the program prints the hostname of the running container using System.out.println("LocalStack running at: " + localStack.getHost())
, which helps verify that LocalStack is successfully started and accessible.
3. Code Example
Create a Spring Boot application using Spring Initializr.
3.1 Adding Dependencies
Add the following dependencies in pom.xml
file:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | < dependencies > <!-- Spring Boot Web --> < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > <!-- Spring Boot Messaging for AWS SQS --> < dependency > < groupId >org.springframework.cloud</ groupId > < artifactId >spring-cloud-aws-messaging</ artifactId > < version >2.2.6.RELEASE</ version > </ dependency > <!-- AWS SDK for Java --> < dependency > < groupId >com.amazonaws</ groupId > < artifactId >aws-java-sdk-sqs</ artifactId > < version >1.12.297</ version > </ dependency > <!-- Lombok for reducing boilerplate code --> < dependency > < groupId >org.projectlombok</ groupId > < artifactId >lombok</ artifactId > < scope >provided</ scope > </ dependency > <!-- Testcontainers for local AWS SQS testing --> < dependency > < groupId >org.testcontainers</ groupId > < artifactId >localstack</ artifactId > < version >1.17.6</ version > < scope >test</ scope > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > </ dependencies > |
3.2 Creating FIFO Queue
LocalStack works well with the AWS Command Line Interface (CLI), allowing developers to interact with AWS services using familiar commands. If you haven’t installed the AWS CLI yet, follow the official installation guide to set it up. Use the following AWS CLI command to create a FIFO queue in LocalStack:
1 | aws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name myqueue.fifo --attributes FifoQueue=true |
When this command runs successfully, AWS SQS (emulated by LocalStack) will return a JSON response containing the queue URL. To verify that the queue was created, use the following command:
1 | aws --endpoint-url=http://localhost:4566 sqs list-queues |
This will return a list of all available queues, including the newly created FIFO queue. You can then send and receive messages from this queue using AWS SDKs, CLI commands, or LocalStack-based integrations.
3.3 AWS SQS Setup Configuration
This configuration sets up AWS SQS using LocalStack.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.example.sqs.config; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.sqs.AmazonSQSAsync; import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SqsConfig { @Bean public AmazonSQSAsync amazonSQSAsync() { return AmazonSQSAsyncClientBuilder.standard() .withEndpointConfiguration( new AwsClientBuilder.EndpointConfiguration( .withCredentials( new AWSStaticCredentialsProvider( new BasicAWSCredentials( "access" , "secret" ))) .build(); } } |
3.3.1 Code Explanation
The SqsConfig
class is a Spring configuration class, annotated with @Configuration
, that defines a bean for integrating with AWS Simple Queue Service (SQS). The amazonSQSAsync
method, annotated with @Bean
, creates and returns an instance of AmazonSQSAsync
using the AmazonSQSAsyncClientBuilder
. It sets the endpoint configuration to http://localhost:4566
, which is the default LocalStack URL for AWS service emulation, and specifies the AWS region as us-east-1
. The method also provides static AWS credentials using AWSStaticCredentialsProvider
and BasicAWSCredentials
, where placeholder values (“access” and “secret”) are used since LocalStack does not require real AWS credentials. This configuration allows Spring applications to interact with a locally hosted SQS instance for testing and development purposes.
3.4 SQS Service (Message Sender & Listener)
This service handles sending messages to the FIFO queue.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.example.sqs.service; import com.amazonaws.services.sqs.AmazonSQSAsync; import com.amazonaws.services.sqs.model.SendMessageRequest; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.UUID; @Service @RequiredArgsConstructor public class SqsMessageService { private final AmazonSQSAsync amazonSQSAsync; public void sendMessage(String message, String messageGroupId) { SendMessageRequest sendMessageRequest = new SendMessageRequest() .withQueueUrl(queueUrl) .withMessageBody(message) .withMessageGroupId(messageGroupId) .withMessageDeduplicationId(UUID.randomUUID().toString()); amazonSQSAsync.sendMessage(sendMessageRequest); } } |
3.4.1 Code Explanation
The SqsMessageService
class is a Spring service, annotated with @Service
, responsible for sending messages to an Amazon SQS FIFO queue. It uses the @RequiredArgsConstructor
annotation from Lombok to automatically inject dependencies, specifically an instance of AmazonSQSAsync
for asynchronous communication with SQS. The queue URL is defined as a constant, pointing to a LocalStack-hosted FIFO queue. The sendMessage
method constructs a SendMessageRequest
, setting the queue URL, message body, and messageGroupId
, which is required for FIFO queues to ensure ordered processing. Additionally, it includes a unique messageDeduplicationId
generated using UUID.randomUUID().toString()
to prevent duplicate message delivery. Finally, the message is sent asynchronously using amazonSQSAsync.sendMessage(sendMessageRequest)
, enabling reliable, ordered, and deduplicated message processing in the FIFO queue.
3.5 Message Listener (Processing Messages)
This listener will process messages as they arrive.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | package com.example.sqs.listener; import org.springframework.cloud.aws.messaging.listener.annotation.SqsListener; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; @Component public class SqsMessageListener { @SqsListener ( "myqueue.fifo" ) public void receiveMessage(String message, @Header ( "MessageGroupId" ) String groupId) { System.out.println( "Processing message from group " + groupId + ": " + message); } } |
3.5.1 Code Explanation
The SqsMessageListener
class is a Spring component, annotated with @Component
, that listens for incoming messages from an Amazon SQS FIFO queue. It uses the @SqsListener("myqueue.fifo")
annotation to automatically subscribe to the myqueue.fifo
queue and process messages as they arrive. The receiveMessage
method takes two parameters: the message body as a String
and the MessageGroupId
, which is extracted from the message headers using the @Header("MessageGroupId")
annotation. This ensures that messages belonging to the same group are processed in order. When a message is received, the method logs its content along with the group ID using System.out.println
, demonstrating how messages are consumed from the FIFO queue while maintaining their ordering guarantees.
3.6 Controller (Exposing API for Sending Messages)
This REST controller provides an endpoint to send messages.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | package com.example.sqs.controller; import com.example.sqs.service.SqsMessageService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping ( "/sqs" ) @RequiredArgsConstructor public class SqsController { private final SqsMessageService sqsMessageService; @PostMapping ( "/send" ) public String sendMessage( @RequestParam String message, @RequestParam String groupId) { sqsMessageService.sendMessage(message, groupId); return "Message sent successfully!" ; } } |
3.6.1 Code Explanation
The SqsController
class is a Spring REST controller, annotated with @RestController
and @RequestMapping("/sqs")
, which exposes an HTTP endpoint for sending messages to an Amazon SQS FIFO queue. It uses the @RequiredArgsConstructor
annotation from Lombok to automatically inject the SqsMessageService
dependency. The sendMessage
method, mapped to POST /sqs/send
using @PostMapping("/send")
, accepts two request parameters: message
(the message body) and groupId
(the message group ID for FIFO ordering). It then calls sqsMessageService.sendMessage(message, groupId)
to send the message to the queue. Upon successful execution, the method returns a simple confirmation message, "Message sent successfully!"
, indicating that the message has been dispatched to SQS.
3.7 Setting up the properties
Include the following properties in the application.properties
file located at src/main/resources/application.properties
.
1 2 | # Server Configuration server.port=8080 |
4. Testing the Application
To start the application, run the main class from your IDE or use the following command:
1 | mvn spring-boot:run |
You can test the API using cURL or Postman. Execute the following cURL command:
1 |
If everything goes well, the following response will be logged on the IDE console:
1 | Processing message from group Group1: HelloWorld |
If you want to send messages to different group IDs, feel free to explore the app.
5. Conclusion
In this article, we explored FIFO queue support in Spring Cloud AWS using LocalStack and Testcontainers for local testing. We configured a Spring Boot application to send and receive messages while ensuring ordered processing with messageGroupId
.