Spring Cloud AWS SQS: SqsTemplate and @SqsListener Example

Learn to send and receive Amazon SQS messages in a Spring Boot app with Spring Cloud AWS: SqsTemplate, @SqsListener, the endpoint property for LocalStack, processing more than 10 messages concurrently with maxConcurrentMessages, acknowledgement modes and FIFO queues.

aws

Spring Cloud AWS is the Spring project that connects a Spring Boot app to AWS services. Its SQS module sends messages to an Amazon SQS queue with SqsTemplate and receives them with @SqsListener. Amazon SQS is a managed message queue, where one part of our system puts a message and another part reads it later, so the two never wait for each other.

We use Spring Cloud AWS SQS when a web request should return at once and the slow work can happen later. For example, a request creates an invoice, and another service emails it a moment later. The starter gives us a ready SqsTemplate bean and converts our objects to JSON and back.

The following example sends one invoice to the queue invoice-queue and receives it in a listener method.

// 1. Send: the record is written to the queue as JSON
SendResult<InvoiceCreated> result = sqsTemplate.send("invoice-queue", new InvoiceCreated(1, "Lokesh", 120));
UUID messageId = result.messageId();      // de34536b-c578-48f6-8358-11ec91e92c79

// 2. Receive: Spring polls the queue and calls this method for every message
@SqsListener("invoice-queue")
public void onInvoiceCreated(InvoiceCreated invoice) {
  log.info("Received invoice {} for {}", invoice.number(), invoice.customer());   // Received invoice 1 for Lokesh
}

Notice that we never call the AWS SDK, and that we use the queue name instead of its URL. The listener gets the record back as a Java object. Next we set up the project, point it at LocalStack, process more than 10 messages at the same time and pick an acknowledgement mode.

1. Spring Cloud AWS Setup

Spring Cloud AWS has its own version number and its own BOM, and each release line works with one Spring Boot line. Spring Cloud AWS 4.x is built for Spring Boot 4.0.x, so a Spring Boot 3.5 project stays on Spring Cloud AWS 3.4.x.

Spring Cloud AWSSpring BootSpring Framework
3.3.x3.4.x6.2.x
3.4.x3.5.x6.2.x
4.x4.0.x7.0.x

We import the BOM in dependencyManagement and add the SQS starter, which brings the AWS SDK client. We also add spring-boot-starter-jackson, because the JSON conversion uses Jackson and a plain Spring Boot 4 app does not have it on the classpath.

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.awspring.cloud</groupId>
      <artifactId>spring-cloud-aws-dependencies</artifactId>
      <version>4.2.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>io.awspring.cloud</groupId>
    <artifactId>spring-cloud-aws-starter-sqs</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jackson</artifactId>
  </dependency>
</dependencies>

The SQS client needs credentials and a region, and all properties start with spring.cloud.aws. In production we leave the two credential properties out, so the AWS SDK reads them from environment variables or from the IAM role of the server.

spring.cloud.aws.credentials.access-key=test
spring.cloud.aws.credentials.secret-key=test
spring.cloud.aws.region.static=us-east-1

2. SQS Endpoint Property for LocalStack

By default, the SQS client calls the real AWS endpoint of the configured region. During development we point it at LocalStack instead, which runs a copy of the SQS API in a Docker container on our machine. The property spring.cloud.aws.sqs.endpoint overrides the SQS endpoint, and spring.cloud.aws.endpoint overrides the endpoint of every AWS client at once. LocalStack accepts any access key and secret key.

spring.cloud.aws.sqs.endpoint=http://localhost:4566

# Create a queue on first use (CREATE is the default; use FAIL in production)
spring.cloud.aws.sqs.queue-not-found-strategy=CREATE

The second property decides what happens when the queue does not exist. With CREATE the framework creates the queue on first use, with FAIL the app throws an exception at startup, and with IGNORE the listener logs a warning and skips the queue.

docker run -d --name localstack -p 4566:4566 localstack/localstack:4.12.0

In real AWS we create the queue in the SQS console and set the strategy to FAIL.

3. Spring Cloud AWS SQS Example

The following example is an invoice service. A producer sends an InvoiceCreated message for every new invoice, and a listener emails the invoice to the customer. The payload is a Java record, which goes into the queue as JSON.

public record InvoiceCreated(int number, String customer, int amount) {
}

The project uses Spring Boot 4.0.8, Spring Cloud AWS 4.2.0, Java 25, LocalStack 4.12.0 and Testcontainers 2.0.5. The complete code, with a test that starts LocalStack, is in the GitHub repository.

Flow diagram of a producer calling SqsTemplate.send(), 25 messages in an Amazon SQS queue, two polls of 10 messages, and an @SqsListener container with 20 worker threads that acknowledge each message
The producer writes JSON messages with SqsTemplate, the listener polls 10 at a time and processes up to maxConcurrentMessages of them in parallel.

4. Sending Messages with SqsTemplate

Spring Boot creates one SqsTemplate bean for us. The bean is thread-safe, so we inject the same one into every class that sends messages. The short form of send() takes the queue name or URL and the payload. The lambda form also takes extras such as a header or a delay.

SendResult<InvoiceCreated> result = sqsTemplate.send(to -> to
    .queue("invoice-queue")
    .payload(invoice)
    .header("source", "web"));

UUID messageId = result.messageId();     // de34536b-c578-48f6-8358-11ec91e92c79
String queue = result.endpoint();        // invoice-queue

Each header becomes an SQS message attribute, and the listener reads it with @Header. Sending many messages one by one costs one HTTP request each. The sendMany() method uses the SQS batch API, which accepts at most 10 messages per request. Since Spring Cloud AWS 4.2.0 the template splits a longer list into batches of 10, so 25 invoices become three requests.

List<Message<InvoiceCreated>> messages = invoices.stream()
    .map(invoice -> MessageBuilder.withPayload(invoice).setHeader("source", "import").build())
    .toList();

SendResult.Batch<InvoiceCreated> batch = sqsTemplate.sendMany("invoice-queue", messages);
int sent = batch.successful().size();    // 25
int failed = batch.failed().size();      // 0

If one message in a batch fails, the template throws SendBatchOperationFailedException, which lists the failed messages.

5. Receiving Messages with @SqsListener

A method annotated with @SqsListener becomes a message listener. Spring Cloud AWS creates a listener container for it, which polls the queue in a loop. The container uses long polling, so a poll waits up to 10 seconds for messages instead of returning empty at once. For every message the container converts the JSON body to the parameter type and calls our method on a worker thread.

@SqsListener(value = "invoice-queue", id = "invoices")
public void onInvoiceCreated(InvoiceCreated invoice, @Header("source") String source) {
  log.info("Received invoice {} for {} from {}", invoice.number(), invoice.customer(), source);
  emailSender.send(invoice);
}

The id names the container and its threads, which helps when we read the logs. The method parameters decide what we get.

ParameterWhat the listener method gets
InvoiceCreated invoiceThe payload, converted from JSON.
Message<InvoiceCreated> messageThe payload plus the headers.
@Header(“source”) String sourceOne message attribute. Add required = false when the sender may leave it out.
Acknowledgement ackDeletes the message in MANUAL mode (see section 7).
[     invoices-1] com.howtodoinjava.sqs.InvoiceListener    : Received invoice 1 for Lokesh from web
[     invoices-1] com.howtodoinjava.sqs.EmailSender        : Emailed invoice 1 to Lokesh

6. Processing More Than 10 SQS Messages Concurrently

By default, a listener container processes at most 10 messages of a queue at the same time. The reason is that maxConcurrentMessages defaults to 10, and one poll returns at most 10 messages. Two settings change the throughput.

SettingDefaultWhat it does
maxConcurrentMessages10The number of messages of one queue processed at the same time, which is also the thread count per queue.
maxMessagesPerPoll10The number of messages asked for in one poll. SQS returns at most 10 per request.
pollTimeout10 sThe time one poll waits for messages.

We set the values in the annotation for one listener, or in application.properties for every listener in the app. The annotation value wins, and the limit applies per queue.

@SqsListener(value = "invoice-queue", id = "invoices", maxConcurrentMessages = "20", maxMessagesPerPoll = "10")
public void onInvoiceCreated(InvoiceCreated invoice, @Header("source") String source) {
spring.cloud.aws.sqs.listener.max-concurrent-messages=10
spring.cloud.aws.sqs.listener.max-messages-per-poll=10
spring.cloud.aws.sqs.listener.poll-timeout=10s

With 25 invoices in the queue, the container polls 10 more before the first 10 finish, and the log shows threads invoices-1 to invoices-20 working at the same time.

[     invoices-1] com.howtodoinjava.sqs.InvoiceListener    : Received invoice 121 for Customer 121 from import
...
[    invoices-11] com.howtodoinjava.sqs.InvoiceListener    : Received invoice 106 for Customer 106 from import
...
[    invoices-20] com.howtodoinjava.sqs.InvoiceListener    : Received invoice 115 for Customer 115 from import
[     invoices-1] com.howtodoinjava.sqs.EmailSender        : Emailed invoice 121 to Customer 121

Each message in flight takes one thread, so a value such as 200 means 200 threads per queue. For listeners that mostly wait on a database or an HTTP call, Spring Cloud AWS 4.1 and later can run the container on virtual threads.

7. Acknowledgement Modes

In SQS, acknowledging a message means deleting it from the queue. A received message only becomes invisible for the visibility timeout, which is 30 seconds by default. If nobody deletes the message in that time, SQS delivers it again. The listener container deletes messages for us, and the acknowledgementMode decides when.

ModeWhen the message is deletedUse it when
ON_SUCCESS (default)After our method returns without an exception. A failed message comes back after the visibility timeout.We want a retry on every error.
ALWAYSAfter our method returns or throws.A retry would not help, such as a malformed message.
MANUALOnly when we call acknowledge() on the Acknowledgement parameter.We decide per message, such as after a database commit.

We set the mode in the annotation. In MANUAL mode the method gets an Acknowledgement parameter, and a message we never acknowledge comes back after the visibility timeout.

@SqsListener(value = "invoice-audit-queue", id = "audit", acknowledgementMode = "MANUAL")
public void onInvoiceCreated(Message<InvoiceCreated> message, Acknowledgement ack) {
  InvoiceCreated invoice = message.getPayload();
  log.info("Audit record written for invoice {}", invoice.number());
  ack.acknowledge();   // deletes the message from the queue
}

A message that fails on every retry comes back forever. The fix is a dead-letter queue, a second queue that SQS moves a message to after a chosen number of receives. We set it on the main queue, and the listener needs no change.

AWS SQS console section for enabling a dead-letter queue on the main queue, with the target queue and the maximum receives field
The main queue moves a message to the dead-letter queue after Maximum receives failed attempts.

8. FIFO Queues in Brief

A FIFO queue delivers the messages of one message group in the order they were sent, and each message once. We give every message a messageGroupId, such as the customer, and a messageDeduplicationId, such as the invoice number. Two messages with the same deduplication id within 5 minutes count as one.

SendResult<InvoiceCreated> result = sqsTemplate.send(to -> to
    .queue("invoice-queue.fifo")
    .payload(invoice)
    .messageGroupId(invoice.customer())
    .messageDeduplicationId(String.valueOf(invoice.number())));

The listener side needs no FIFO code. A queue name ending with .fifo is set up as a FIFO queue, and the container processes the messages of one group one after the other, with different groups in parallel.

9. Testing with Testcontainers

In a test we do not want a container that someone has to start by hand. Testcontainers starts LocalStack from the test, and @ServiceConnection points every AWS client of the app at that container, so the test needs no endpoint property. The test scope also needs spring-boot-testcontainers, spring-cloud-aws-testcontainers, testcontainers-localstack and testcontainers-junit-jupiter. The listener runs on another thread, so the test waits with Awaitility until the message shows up.

@Container
@ServiceConnection
static LocalStackContainer localStack =
    new LocalStackContainer(DockerImageName.parse("localstack/localstack:4.12.0"));

10. Conclusion

Spring Cloud AWS SQS takes the AWS SDK calls out of our code. We send with SqsTemplate by queue name and receive with a @SqsListener method that gets our record back. One property, spring.cloud.aws.sqs.endpoint, points the app at LocalStack.

When the queue grows, maxConcurrentMessages raises the number of messages in flight above 10, and the acknowledgement mode decides whether a failed message comes back for a retry.

11. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Hello , After I clone the project and modify aws configuration according to my account i got below error
    2025-01-12T23:45:26.114+05:30 WARN 20148 — [      main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization – cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean ‘io.awspring.cloud.messaging.internalEndpointRegistryBeanName’

      • Please find below full log trace:
        2025-01-16T07:00:35.302+05:30 WARN 9148 — [      main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization – cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean ‘io.awspring.cloud.messaging.internalEndpointRegistryBeanName’
        2025-01-16T07:00:40.319+05:30 INFO 9148 — [      main] o.apache.catalina.core.StandardService  : Stopping service [Tomcat]
        2025-01-16T07:00:40.366+05:30 INFO 9148 — [      main] .s.b.a.l.ConditionEvaluationReportLogger : 

        Error starting ApplicationContext. To display the condition evaluation report re-run your application with ‘debug’ enabled.
        2025-01-16T07:00:40.410+05:30 ERROR 9148 — [      main] o.s.boot.SpringApplication        : Application run failed

        org.springframework.context.ApplicationContextException: Failed to start bean ‘io.awspring.cloud.messaging.internalEndpointRegistryBeanName’
        at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-6.0.9.jar:6.0.9]
        at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-6.0.9.jar:6.0.9]
        at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
        at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-6.0.9.jar:6.0.9]
        at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-6.0.9.jar:6.0.9]
        at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:958) ~[spring-context-6.0.9.jar:6.0.9]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:611) ~[spring-context-6.0.9.jar:6.0.9]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.1.0.jar:3.1.0]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733) ~[spring-boot-3.1.0.jar:3.1.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435) ~[spring-boot-3.1.0.jar:3.1.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-3.1.0.jar:3.1.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1305) ~[spring-boot-3.1.0.jar:3.1.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1294) ~[spring-boot-3.1.0.jar:3.1.0]
        at com.example.springawssqsexample.SpringAwsSqsExampleApplication.main(SpringAwsSqsExampleApplication.java:16) ~[classes/:na]
        Caused by: java.util.concurrent.CompletionException: io.awspring.cloud.sqs.QueueAttributesResolvingException: Error resolving attributes for queue HowToDoInJava with strategy CREATE and queueAttributesNames []
        at java.base/java.util.concurrent.CompletableFuture.encodeRelay(CompletableFuture.java:368) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeRelay(CompletableFuture.java:377) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1152) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.utils.CompletableFutureUtils.lambda$forwardExceptionTo$0(CompletableFutureUtils.java:79) ~[utils-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncApiCallMetricCollectionStage.lambda$execute$0(AsyncApiCallMetricCollectionStage.java:54) ~[sdk-core-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncApiCallTimeoutTrackingStage.lambda$execute$2(AsyncApiCallTimeoutTrackingStage.java:67) ~[sdk-core-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.utils.CompletableFutureUtils.lambda$forwardExceptionTo$0(CompletableFutureUtils.java:79) ~[utils-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeAttemptExecute(AsyncRetryableStage.java:103) ~[sdk-core-2.20.55.jar:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeRetryExecute(AsyncRetryableStage.java:184) ~[sdk-core-2.20.55.jar:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.lambda$attemptExecute$1(AsyncRetryableStage.java:159) ~[sdk-core-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.utils.CompletableFutureUtils.lambda$forwardExceptionTo$0(CompletableFutureUtils.java:79) ~[utils-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$null$0(MakeAsyncHttpRequestStage.java:103) ~[sdk-core-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162) ~[na:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$executeHttpRequest$3(MakeAsyncHttpRequestStage.java:165) ~[sdk-core-2.20.55.jar:na]
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]
        Caused by: io.awspring.cloud.sqs.QueueAttributesResolvingException: Error resolving attributes for queue HowToDoInJava with strategy CREATE and queueAttributesNames []
        at io.awspring.cloud.sqs.QueueAttributesResolver.wrapException(QueueAttributesResolver.java:90) ~[spring-cloud-aws-sqs-3.0.0.jar:3.0.0]
        at java.base/java.util.concurrent.CompletableFuture.uniExceptionally(CompletableFuture.java:990) ~[na:na]
        at java.base/java.util.concurrent.CompletableFuture$UniExceptionally.tryFire(CompletableFuture.java:974) ~[na:na]
        … 46 common frames omitted
        Caused by: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: Acquire operation took longer than the configured maximum time. This indicates that a request cannot get a connection from the pool within the specified maximum time. This can be due to high request rate.
        Consider taking any of the following actions to mitigate the issue: increase max connections, increase acquire timeout, or slowing the request rate.
        Increasing the max connections can increase client throughput (unless the network interface is already fully utilized), but can eventually start to hit operation system limitations on the number of file descriptors used by the process. If you already are fully utilizing your network interface or cannot further increase your connection count, increasing the acquire timeout gives extra time for requests to acquire a connection before timing out. If the connections doesn’t free up, the subsequent requests will still timeout.
        If the above mechanisms are not able to fix the issue, try smoothing out your requests so that large traffic bursts cannot overload the client, being more efficient with the number of times you need to call AWS, or by increasing the number of hosts sending requests.
        at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:111) ~[sdk-core-2.20.55.jar:na]
        at software.amazon.awssdk.core.exception.SdkClientException.create(SdkClientException.java:47) ~[sdk-core-2.20.55.jar:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper.setLastException(RetryableStageHelper.java:223) ~[sdk-core-2.20.55.jar:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper.setLastException(RetryableStageHelper.java:218) ~[sdk-core-2.20.55.jar:na]
        at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeRetryExecute(AsyncRetryableStage.java:182) ~[sdk-core-2.20.55.jar:na]
        … 22 common frames omitted
        Caused by: java.lang.Throwable: Acquire operation took longer than the configured maximum time. This indicates that a request cannot get a connection from the pool within the specified maximum time. This can be due to high request rate.
        Consider taking any of the following actions to mitigate the issue: increase max connections, increase acquire timeout, or slowing the request rate.
        Increasing the max connections can increase client throughput (unless the network interface is already fully utilized), but can eventually start to hit operation system limitations on the number of file descriptors used by the process. If you already are fully utilizing your network interface or cannot further increase your connection count, increasing the acquire timeout gives extra time for requests to acquire a connection before timing out. If the connections doesn’t free up, the subsequent requests will still timeout.
        If the above mechanisms are not able to fix the issue, try smoothing out your requests so that large traffic bursts cannot overload the client, being more efficient with the number of times you need to call AWS, or by increasing the number of hosts sending requests.
        at software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.decorateException(NettyUtils.java:69) ~[netty-nio-client-2.20.55.jar:na]
        at software.amazon.awssdk.http.nio.netty.internal.NettyRequestExecutor.handleFailure(NettyRequestExecutor.java:309) ~[netty-nio-client-2.20.55.jar:na]
        at software.amazon.awssdk.http.nio.netty.internal.NettyRequestExecutor.makeRequestListener(NettyRequestExecutor.java:189) ~[netty-nio-client-2.20.55.jar:na]
        at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:590) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:557) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.DefaultPromise.access$200(DefaultPromise.java:35) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.DefaultPromise$1.run(DefaultPromise.java:503) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        … 1 common frames omitted
        Caused by: java.util.concurrent.TimeoutException: Acquire operation took longer than 10000 milliseconds.
        at software.amazon.awssdk.http.nio.netty.internal.HealthCheckedChannelPool.timeoutAcquire(HealthCheckedChannelPool.java:77) ~[netty-nio-client-2.20.55.jar:na]
        at software.amazon.awssdk.http.nio.netty.internal.HealthCheckedChannelPool.lambda$acquire$0(HealthCheckedChannelPool.java:67) ~[netty-nio-client-2.20.55.jar:na]
        at io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:153) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
        … 7 common frames omitted

        • Please verify the followings:

          There is a queue created in AWS SNS with name: HowToDoInJava
          The AWS credentials or permissions are configured properly.
          Double check the queue URL as defined in spring.cloud.aws.sqs.endpoint.

          • Thanks for reply, I haven’t configure SNS. My focus only to use sqs and IAM user accesskey,secretkey, endpoint of sqs setup correctly.

            Queue name is enough? I dont see this spring.cloud.aws.sqs.endpoint.property in application.properties file could please rerun from your side once again

             sqsSendOptions
                  .queue(“HowToDoInJava”)
                  .payload(message)

          • Yes, it is working fine. I tested again and without any code changes, it worked. The only change I made was that AWS key and secrets are now referred from environment properties.

          • And apologies for suggesting changes in SNS. I wanted to say SQS. Please make sure the queue exists there.

  2. Hi ,

    the contextLoad() test method in the application fails only when the io.awspring.cloud:spring-cloud-aws-starter-sqs is added as the dependency. any idea? i dont think any other dependency except the aws java sdk has the http clients in them.

    Caused by: software.amazon.awssdk.core.exception.SdkClientException: Multiple HTTP implementations were found on the classpath. To avoid non-deterministic loading implementations, please explicitly provide an HTTP client via the client builders, set the software.amazon.awssdk.http.service.impl system property with the FQCN of the HTTP service to use as the default, or remove all but one HTTP implementation from the classpath. The multiple implementations found were: [software.amazon.awssdk.http.apache.ApacheSdkHttpService,software.amazon.awssdk.http.urlconnection.UrlConnectionSdkHttpService]

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.