skip to Main Content

I am using Spring Cloud Stream with Azure Service Bus. These are my relevant versions and dependencies:

 <spring-cloud-azure.version>4.3.0</spring-cloud-azure.version>
 <spring-cloud.version>2021.0.3</spring-cloud.version>

 <dependency>
     <groupId>com.azure.spring</groupId>
     <artifactId>spring-cloud-azure-starter</artifactId>
 </dependency>

 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-stream</artifactId>
 </dependency>

 <dependency>
     <groupId>com.azure.spring</groupId>
     <artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId>
     <version>4.3.0</version>
 </dependency>

I want to implement retry policy, where message that causes an exception gets retried multiple times, with some delay between the retries.

From the documentation, I see that there are some configuration settings available, however it seems the settings are not taken into account.

The configuration properties: https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/spring-cloud-azure-appendix

My application.yml:

spring:
  cloud:
    stream:
      bindings:
        onReceive-in-0:
          destination: subscriber-1-input-queue

    azure:
      servicebus:
        consumer:
          retry:
            exponential:
              base-delay: 100
              max-delay: 1000
              max-retries: 5
            fixed:
              delay: 100
              max-retries: 5
            mode: exponential
        connection-string: Endpoint=...

My consumer:

@Slf4j
@Component
public class ValuesConsumer {

    @Bean
    public Consumer<String> onReceive() {
        return (message) -> {

          log.info("Received the value {} in Consumer", message);

            switch (message) {
                case "IntegrationException" -> throw new IntegrationException();
                case "PoisonPillException" -> throw new PoisonPillException();
                case "ProcessingException" -> throw new ProcessingException();
                case "ValidationException" -> throw new ValidationException();
            }

        };
    }
}

When I throw the exception, message processing gets retried 10 times without any delays – no retry logic actually happens, just message gets abandoned and then received again from the queue.

This is the exception stacktrace, which is repeated 10 times:

2022-08-31 11:26:45.819  INFO 10544 --- [oundedElastic-3] com.demoasb.ValuesConsumer        : Received the value IntegrationException in Consumer
2022-08-31 11:26:45.823 ERROR 10544 --- [oundedElastic-3] .s.i.s.i.ServiceBusInboundChannelAdapter : Error in the operation USER_CALLBACK occurred on entity subscriber-1-input-queue. Error: {}

com.azure.messaging.servicebus.ServiceBusException: Dispatcher has no subscribers for channel 'application.errorChannel'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: error occurred in message handler [org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder$1@75a1e7f9]; nested exception is com.demoasb.exceptions.IntegrationException, failedMessage=GenericMessage [payload=byte[20], headers={azure_service_bus_expires_at=2022-09-14T09:26:45.740Z, azure_partition_key=415274637, azure_service_bus_received_message_context=com.azure.messaging.servicebus.ServiceBusReceivedMessageContext@6c625687, azure_service_bus_message_id=1981cbaf-68e3-62ec-b28e-9aaf5be48487, azure_service_bus_enqueued_sequence_number=46, azure_service_bus_enqueued_time=2022-08-31T09:26:45.740Z, azure_service_bus_lock_token=54cb3555-6253-4ef6-a858-de894358e56b, azure_service_bus_sequence_number=63, target-protocol=kafka, actionType=EventA, azure_service_bus_time_to_live=PT336H, azure_service_bus_delivery_count=0, azure_service_bus_state=ACTIVE, id=a896f227-28d2-c65e-afa3-b8e1e41b99c2, contentType=application/json, azure_service_bus_locked_until=2022-08-31T09:27:15.755Z, timestamp=1661938005819}], headers={id=d6a9c4de-a566-fb2d-f14f-568a3a2a4e43, timestamp=1661938005821}] for original GenericMessage [payload=byte[20], headers={azure_service_bus_expires_at=2022-09-14T09:26:45.740Z, azure_partition_key=415274637, azure_service_bus_received_message_context=com.azure.messaging.servicebus.ServiceBusReceivedMessageContext@6c625687, azure_service_bus_message_id=1981cbaf-68e3-62ec-b28e-9aaf5be48487, azure_service_bus_enqueued_sequence_number=46, azure_service_bus_enqueued_time=2022-08-31T09:26:45.740Z, azure_service_bus_lock_token=54cb3555-6253-4ef6-a858-de894358e56b, azure_service_bus_sequence_number=63, target-protocol=kafka, actionType=EventA, azure_service_bus_time_to_live=PT336H, azure_service_bus_delivery_count=0, azure_service_bus_state=ACTIVE, id=a896f227-28d2-c65e-afa3-b8e1e41b99c2, contentType=application/json, azure_service_bus_locked_until=2022-08-31T09:27:15.755Z, timestamp=1661938005819}]
    at com.azure.messaging.servicebus.ServiceBusProcessorClient$1.onNext(ServiceBusProcessorClient.java:346) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at com.azure.messaging.servicebus.ServiceBusProcessorClient$1.onNext(ServiceBusProcessorClient.java:317) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.runAsync(FluxPublishOn.java:440) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.run(FluxPublishOn.java:527) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.scheduler.ImmediateScheduler$ImmediateSchedulerWorker.schedule(ImmediateScheduler.java:84) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.trySchedule(FluxPublishOn.java:312) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.onNext(FluxPublishOn.java:237) ~[reactor-core-3.4.22.jar:3.4.22]
    at com.azure.messaging.servicebus.FluxAutoComplete$AutoCompleteSubscriber.hookOnNext(FluxAutoComplete.java:94) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at com.azure.messaging.servicebus.FluxAutoComplete$AutoCompleteSubscriber.hookOnNext(FluxAutoComplete.java:55) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at reactor.core.publisher.BaseSubscriber.onNext(BaseSubscriber.java:160) ~[reactor-core-3.4.22.jar:3.4.22]
    at com.azure.messaging.servicebus.FluxAutoLockRenew$LockRenewSubscriber.hookOnNext(FluxAutoLockRenew.java:178) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at com.azure.messaging.servicebus.FluxAutoLockRenew$LockRenewSubscriber.hookOnNext(FluxAutoLockRenew.java:79) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at reactor.core.publisher.BaseSubscriber.onNext(BaseSubscriber.java:160) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.4.22.jar:3.4.22]
    at com.azure.messaging.servicebus.implementation.ServiceBusReceiveLinkProcessor.drainQueue(ServiceBusReceiveLinkProcessor.java:516) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at com.azure.messaging.servicebus.implementation.ServiceBusReceiveLinkProcessor.drain(ServiceBusReceiveLinkProcessor.java:475) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at com.azure.messaging.servicebus.implementation.ServiceBusReceiveLinkProcessor.lambda$onNext$2(ServiceBusReceiveLinkProcessor.java:227) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    at reactor.core.publisher.LambdaSubscriber.onNext(LambdaSubscriber.java:160) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.runAsync(FluxPublishOn.java:440) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.run(FluxPublishOn.java:527) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) ~[reactor-core-3.4.22.jar:3.4.22]
    at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) ~[reactor-core-3.4.22.jar:3.4.22]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[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:833) ~[na:na]
Caused by: org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application.errorChannel'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: error occurred in message handler [org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder$1@75a1e7f9]; nested exception is com.demoasb.exceptions.IntegrationException, failedMessage=GenericMessage [payload=byte[20], headers={azure_service_bus_expires_at=2022-09-14T09:26:45.740Z, azure_partition_key=415274637, azure_service_bus_received_message_context=com.azure.messaging.servicebus.ServiceBusReceivedMessageContext@6c625687, azure_service_bus_message_id=1981cbaf-68e3-62ec-b28e-9aaf5be48487, azure_service_bus_enqueued_sequence_number=46, azure_service_bus_enqueued_time=2022-08-31T09:26:45.740Z, azure_service_bus_lock_token=54cb3555-6253-4ef6-a858-de894358e56b, azure_service_bus_sequence_number=63, target-protocol=kafka, actionType=EventA, azure_service_bus_time_to_live=PT336H, azure_service_bus_delivery_count=0, azure_service_bus_state=ACTIVE, id=a896f227-28d2-c65e-afa3-b8e1e41b99c2, contentType=application/json, azure_service_bus_locked_until=2022-08-31T09:27:15.755Z, timestamp=1661938005819}], headers={id=d6a9c4de-a566-fb2d-f14f-568a3a2a4e43, timestamp=1661938005821}] for original GenericMessage [payload=byte[20], headers={azure_service_bus_expires_at=2022-09-14T09:26:45.740Z, azure_partition_key=415274637, azure_service_bus_received_message_context=com.azure.messaging.servicebus.ServiceBusReceivedMessageContext@6c625687, azure_service_bus_message_id=1981cbaf-68e3-62ec-b28e-9aaf5be48487, azure_service_bus_enqueued_sequence_number=46, azure_service_bus_enqueued_time=2022-08-31T09:26:45.740Z, azure_service_bus_lock_token=54cb3555-6253-4ef6-a858-de894358e56b, azure_service_bus_sequence_number=63, target-protocol=kafka, actionType=EventA, azure_service_bus_time_to_live=PT336H, azure_service_bus_delivery_count=0, azure_service_bus_state=ACTIVE, id=a896f227-28d2-c65e-afa3-b8e1e41b99c2, contentType=application/json, azure_service_bus_locked_until=2022-08-31T09:27:15.755Z, timestamp=1661938005819}]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:76) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:317) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:272) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:187) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:166) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:109) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:457) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.doProduceOutput(AbstractMessageProducingHandler.java:325) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:268) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:232) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:142) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:56) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.dispatcher.BroadcastingDispatcher.invokeHandler(BroadcastingDispatcher.java:222) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.dispatcher.BroadcastingDispatcher.dispatch(BroadcastingDispatcher.java:178) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:317) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:272) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:187) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:166) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:109) ~[spring-messaging-5.3.22.jar:5.3.22]
    at org.springframework.integration.endpoint.MessageProducerSupport.sendErrorMessageIfNecessary(MessageProducerSupport.java:262) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:219) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at com.azure.spring.integration.servicebus.inbound.ServiceBusInboundChannelAdapter.access$600(ServiceBusInboundChannelAdapter.java:73) ~[spring-integration-azure-servicebus-4.3.0.jar:4.3.0]
    at com.azure.spring.integration.servicebus.inbound.ServiceBusInboundChannelAdapter$IntegrationRecordMessageListener.onMessage(ServiceBusInboundChannelAdapter.java:201) ~[spring-integration-azure-servicebus-4.3.0.jar:4.3.0]
    at com.azure.spring.integration.servicebus.inbound.ServiceBusInboundChannelAdapter$IntegrationRecordMessageListener.onMessage(ServiceBusInboundChannelAdapter.java:186) ~[spring-integration-azure-servicebus-4.3.0.jar:4.3.0]
    at com.azure.messaging.servicebus.ServiceBusProcessorClient$1.onNext(ServiceBusProcessorClient.java:343) ~[azure-messaging-servicebus-7.9.1.jar:7.9.1]
    ... 28 common frames omitted
Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
    at org.springframework.integration.dispatcher.BroadcastingDispatcher.dispatch(BroadcastingDispatcher.java:153) ~[spring-integration-core-5.5.14.jar:5.5.14]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72) ~[spring-integration-core-5.5.14.jar:5.5.14]
    ... 55 common frames omitted

2022-08-31 11:26:45.824  WARN 10544 --- [oundedElastic-3] c.a.m.s.ServiceBusProcessorClient        : Error when processing message. Abandoning message.
Dispatcher has no subscribers for channel 'application.errorChannel'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: error occurred in message handler [org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder$1@75a1e7f9]; nested exception is com.demoasb.exceptions.IntegrationException, failedMessage=GenericMessage [payload=byte[20], headers={azure_service_bus_expires_at=2022-09-14T09:26:45.740Z, azure_partition_key=415274637, azure_service_bus_received_message_context=com.azure.messaging.servicebus.ServiceBusReceivedMessageContext@6c625687, azure_service_bus_message_id=1981cbaf-68e3-62ec-b28e-9aaf5be48487, azure_service_bus_enqueued_sequence_number=46, azure_service_bus_enqueued_time=2022-08-31T09:26:45.740Z, azure_service_bus_lock_token=54cb3555-6253-4ef6-a858-de894358e56b, azure_service_bus_sequence_number=63, target-protocol=kafka, actionType=EventA, azure_service_bus_time_to_live=PT336H, azure_service_bus_delivery_count=0, azure_service_bus_state=ACTIVE, id=a896f227-28d2-c65e-afa3-b8e1e41b99c2, contentType=application/json, azure_service_bus_locked_until=2022-08-31T09:27:15.755Z, timestamp=1661938005819}], headers={id=d6a9c4de-a566-fb2d-f14f-568a3a2a4e43, timestamp=1661938005821}] for original GenericMessage [payload=byte[20], headers={azure_service_bus_expires_at=2022-09-14T09:26:45.740Z, azure_partition_key=415274637, azure_service_bus_received_message_context=com.azure.messaging.servicebus.ServiceBusReceivedMessageContext@6c625687, azure_service_bus_message_id=1981cbaf-68e3-62ec-b28e-9aaf5be48487, azure_service_bus_enqueued_sequence_number=46, azure_service_bus_enqueued_time=2022-08-31T09:26:45.740Z, azure_service_bus_lock_token=54cb3555-6253-4ef6-a858-de894358e56b, azure_service_bus_sequence_number=63, target-protocol=kafka, actionType=EventA, azure_service_bus_time_to_live=PT336H, azure_service_bus_delivery_count=0, azure_service_bus_state=ACTIVE, id=a896f227-28d2-c65e-afa3-b8e1e41b99c2, contentType=application/json, azure_service_bus_locked_until=2022-08-31T09:27:15.755Z, timestamp=1661938005819}]
2022-08-31 11:26:45.952  INFO 10544 --- [oundedElastic-3] c.a.m.s.i.ServiceBusReceiveLinkProcessor : {"az.sdk.message":"Adding credits.","prefetch":0,"requested":2,"linkCredits":0,"expectedTotalCredit":2,"queuedMessages":1,"creditsToAdd":1,"messageQueueSize":0}

How to correctly implement the delay strategy? Is anything missing from my configuration?

2

Answers


  1. To set the retry policy for Service Bus binder, you need to configure in the binding level, for example,

    spring:
      cloud:
        stream:
          servicebus:
            bindings:
              onReceive-in-0:
                consumer:
                  retry:
                    fixed:
                      delay: 100
                      max-retries: 5
                    mode: fixed
        
    
    Login or Signup to reply.
  2. You can set the retry strategy at multiple levels.

    spring:
      cloud:
        azure:
          # At root level
          retry:
            amqp:
              # How long to wait until a timeout
              try-timeout: PT10S
            # Retry backoff mode: exponential / fixed
            mode: exponential
            # Configuration for the 'exponential' backoff mode
            exponential:
              # Amount of time to wait between retry attempts
              base-delay: PT0.5S
              # Maximum permissible amount of time between retry attempts
              max-delay: PT2S
              # The maximum number of attempts
              max-retries: 3
          servicebus:
            consumer:
              # At consumer level
              retry:
                mode: Exponential
                exponential:
                  # Amount of time to wait between retry attempts
                  base-delay: 10
                  # Maximum permissible amount of time between retry attempts
                  max-delay: 5000
                  # The maximum number of attempts
                  max-retries: 10
                fixed:
                  # Amount of time to wait between retry attempts
                  delay:
                  # The maximum number of attempts
                  max-retries:
            processor:
              # At processor level
              retry:
                exponential:
                  # Amount of time to wait between retry attempts
                  base-delay: 10
                  # Maximum permissible amount of time between retry attempts
                  max-delay: 5000
                  # The maximum number of attempts
                  max-retries: 10
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search